summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/amzn/amzn-nvme.c54
-rw-r--r--plugins/amzn/amzn-nvme.h18
-rw-r--r--plugins/dell/dell-nvme.c54
-rw-r--r--plugins/dell/dell-nvme.h18
-rw-r--r--plugins/dera/dera-nvme.c212
-rw-r--r--plugins/dera/dera-nvme.h18
-rw-r--r--plugins/fdp/fdp.c537
-rw-r--r--plugins/fdp/fdp.h24
-rw-r--r--plugins/huawei/huawei-nvme.c389
-rw-r--r--plugins/huawei/huawei-nvme.h19
-rw-r--r--plugins/innogrit/innogrit-nvme.c430
-rw-r--r--plugins/innogrit/innogrit-nvme.h20
-rw-r--r--plugins/innogrit/typedef.h78
-rw-r--r--plugins/inspur/inspur-nvme.c235
-rw-r--r--plugins/inspur/inspur-nvme.h18
-rw-r--r--plugins/inspur/inspur-utils.h175
-rw-r--r--plugins/intel/intel-nvme.c1740
-rw-r--r--plugins/intel/intel-nvme.h25
-rw-r--r--plugins/memblaze/memblaze-nvme.c1227
-rw-r--r--plugins/memblaze/memblaze-nvme.h26
-rw-r--r--plugins/memblaze/memblaze-utils.h221
-rw-r--r--plugins/meson.build29
-rw-r--r--plugins/micron/micron-nvme.c3394
-rw-r--r--plugins/micron/micron-nvme.h36
-rw-r--r--plugins/netapp/netapp-nvme.c654
-rw-r--r--plugins/netapp/netapp-nvme.h19
-rw-r--r--plugins/nvidia/nvidia-nvme.c53
-rw-r--r--plugins/nvidia/nvidia-nvme.h18
-rw-r--r--plugins/ocp/meson.build6
-rw-r--r--plugins/ocp/ocp-clear-fw-update-history.c73
-rw-r--r--plugins/ocp/ocp-clear-fw-update-history.h9
-rw-r--r--plugins/ocp/ocp-nvme.c774
-rw-r--r--plugins/ocp/ocp-nvme.h28
-rw-r--r--plugins/ocp/ocp-utils.c30
-rw-r--r--plugins/ocp/ocp-utils.h18
-rw-r--r--plugins/scaleflux/sfx-nvme.c1213
-rw-r--r--plugins/scaleflux/sfx-nvme.h24
-rw-r--r--plugins/seagate/seagate-diag.h388
-rw-r--r--plugins/seagate/seagate-nvme.c2042
-rw-r--r--plugins/seagate/seagate-nvme.h51
-rw-r--r--plugins/shannon/shannon-nvme.c404
-rw-r--r--plugins/shannon/shannon-nvme.h21
-rw-r--r--plugins/solidigm/meson.build7
-rw-r--r--plugins/solidigm/solidigm-garbage-collection.c115
-rw-r--r--plugins/solidigm/solidigm-garbage-collection.h8
-rw-r--r--plugins/solidigm/solidigm-latency-tracking.c475
-rw-r--r--plugins/solidigm/solidigm-latency-tracking.h9
-rw-r--r--plugins/solidigm/solidigm-nvme.c43
-rw-r--r--plugins/solidigm/solidigm-nvme.h32
-rw-r--r--plugins/solidigm/solidigm-smart.c253
-rw-r--r--plugins/solidigm/solidigm-smart.h8
-rw-r--r--plugins/solidigm/solidigm-telemetry.c183
-rw-r--r--plugins/solidigm/solidigm-telemetry.h8
-rw-r--r--plugins/solidigm/solidigm-telemetry/cod.c194
-rw-r--r--plugins/solidigm/solidigm-telemetry/cod.h9
-rw-r--r--plugins/solidigm/solidigm-telemetry/config.c44
-rw-r--r--plugins/solidigm/solidigm-telemetry/config.h10
-rw-r--r--plugins/solidigm/solidigm-telemetry/data-area.c424
-rw-r--r--plugins/solidigm/solidigm-telemetry/data-area.h11
-rw-r--r--plugins/solidigm/solidigm-telemetry/header.c199
-rw-r--r--plugins/solidigm/solidigm-telemetry/header.h9
-rw-r--r--plugins/solidigm/solidigm-telemetry/meson.build6
-rw-r--r--plugins/solidigm/solidigm-telemetry/telemetry-log.h31
-rw-r--r--plugins/toshiba/toshiba-nvme.c583
-rw-r--r--plugins/toshiba/toshiba-nvme.h21
-rw-r--r--plugins/transcend/transcend-nvme.c90
-rw-r--r--plugins/transcend/transcend-nvme.h21
-rw-r--r--plugins/virtium/virtium-nvme.c1049
-rw-r--r--plugins/virtium/virtium-nvme.h22
-rw-r--r--plugins/wdc/wdc-nvme.c11100
-rw-r--r--plugins/wdc/wdc-nvme.h53
-rw-r--r--plugins/wdc/wdc-utils.c166
-rw-r--r--plugins/wdc/wdc-utils.h79
-rw-r--r--plugins/ymtc/ymtc-nvme.c161
-rw-r--r--plugins/ymtc/ymtc-nvme.h25
-rw-r--r--plugins/ymtc/ymtc-utils.h81
-rw-r--r--plugins/zns/zns.c1290
-rw-r--r--plugins/zns/zns.h32
78 files changed, 31673 insertions, 0 deletions
diff --git a/plugins/amzn/amzn-nvme.c b/plugins/amzn/amzn-nvme.c
new file mode 100644
index 0000000..e04aa53
--- /dev/null
+++ b/plugins/amzn/amzn-nvme.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.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..f6c4f8b
--- /dev/null
+++ b/plugins/amzn/amzn-nvme.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#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", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/dell/dell-nvme.c b/plugins/dell/dell-nvme.c
new file mode 100644
index 0000000..8ed10e7
--- /dev/null
+++ b/plugins/dell/dell-nvme.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright © 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+
+#define CREATE_CMD
+#include "dell-nvme.h"
+
+#define ARRAY_NAME_LEN 80
+
+struct nvme_vu_id_ctrl_field {
+ __u16 dell_mjr;
+ __u16 dell_mnr;
+ __u16 dell_ter;
+ __u8 reserved0[1018];
+};
+
+static void dell_id_ctrl(__u8 *vs, struct json_object *root)
+{
+ struct nvme_vu_id_ctrl_field *id = (struct nvme_vu_id_ctrl_field *)vs;
+ char array_ver[16] = { 0 };
+ char array_name[ARRAY_NAME_LEN + 1] = {0};
+
+ snprintf(array_ver, sizeof(array_ver), "0x%04x%04x%04x",
+ le16_to_cpu(id->dell_mjr),
+ le16_to_cpu(id->dell_mnr),
+ le16_to_cpu(id->dell_ter));
+
+ memcpy(array_name, vs + sizeof(array_ver), ARRAY_NAME_LEN);
+
+ if (root) {
+ json_object_add_value_string(root, "array_name", strlen(array_name) > 1 ? array_name : "NULL");
+ json_object_add_value_string(root, "array_ver", array_ver);
+ return;
+ }
+
+ printf("array_name : %s\n", strlen(array_name) > 1 ? array_name : "NULL");
+ printf("array_ver : %s\n", array_ver);
+}
+
+static int id_ctrl(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ return __id_ctrl(argc, argv, cmd, plugin, dell_id_ctrl);
+}
diff --git a/plugins/dell/dell-nvme.h b/plugins/dell/dell-nvme.h
new file mode 100644
index 0000000..aaf0de1
--- /dev/null
+++ b/plugins/dell/dell-nvme.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/dell/dell-nvme
+
+#if !defined(DELL_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define DELL_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("dell", "DELL vendor specific extensions", NVME_VERSION),
+ 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
new file mode 100644
index 0000000..9408e50
--- /dev/null
+++ b/plugins/dera/dera-nvme.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "dera-nvme.h"
+
+static int char4_to_int(__u8 *data)
+{
+ int i;
+ int result = 0;
+
+ for (i = 0; i < 4; i++) {
+ result = result << 8;
+ result += data[3 - i];
+ }
+ return result;
+}
+
+struct nvme_dera_smart_info_log
+{
+ __u8 quick_rebuild_cnt0[4];
+ __u8 quick_rebuild_cnt1[4];
+ __u8 full_rebuild_cnt0[4];
+ __u8 full_rebuild_cnt1[4];
+ __u8 raw_rebuild_cnt0[4];
+ __u8 raw_rebuild_cnt1[4];
+ __u8 cap_aged;
+ __u8 cap_aged_ratio;
+ __u8 cap_status;
+ __u8 cap_voltage[4];
+ __u8 cap_charge_ctrl_en;
+ __u8 cap_charge_ctrl_val[2];
+ __u8 cap_charge_max_thr[2];
+ __u8 cap_charge_min_thr[2];
+ __u8 dev_status;
+ __u8 dev_status_up;
+ __u8 nand_erase_err_cnt[4];
+ __u8 nand_program_err_cnt[4];
+ __u8 ddra_1bit_err[2];
+ __u8 ddra_2bit_err[2];
+ __u8 ddrb_1bit_err[2];
+ __u8 ddrb_2bit_err[2];
+ __u8 ddr_err_bit;
+ __u8 pcie_corr_err[2];
+ __u8 pcie_uncorr_err[2];
+ __u8 pcie_fatal_err[2];
+ __u8 pcie_err_bit;
+ __u8 power_level;
+ __u8 current_power[2];
+ __u8 nand_init_fail[2];
+ __u8 fw_loader_version[8];
+ __u8 uefi_driver_version[8];
+ __u8 gpio0_err[2];
+ __u8 gpio5_err[2];
+ __u8 gpio_err_bit[2];
+ __u8 rebuild_percent;
+ __u8 pcie_volt_status;
+ __u8 current_pcie_volt[2];
+ __u8 init_pcie_volt_thr[2];
+ __u8 rt_pcie_volt_thr[2];
+ __u8 init_pcie_volt_low[2];
+ __u8 rt_pcie_volt_low[2];
+ __u8 temp_sensor_abnormal[2];
+ __u8 nand_read_retry_fail_cnt[4];
+ __u8 fw_slot_version[8];
+ __u8 rsved[395];
+};
+
+enum dera_device_status
+{
+ DEVICE_STATUS_READY = 0x00,
+ DEVICE_STATUS_QUICK_REBUILDING = 0x01,
+ DEVICE_STATUS_FULL_REBUILDING = 0x02,
+ DEVICE_STATUS_RAW_REBUILDING = 0x03,
+ DEVICE_STATUS_CARD_READ_ONLY = 0x04,
+ DEVICE_STATUS_FATAL_ERROR = 0x05,
+ DEVICE_STATUS_BUSY = 0x06,
+ DEVICE_STAUTS_LOW_LEVEL_FORMAT = 0x07,
+ DEVICE_STAUTS_FW_COMMITING = 0x08,
+ DEVICE_STAUTS__OVER_TEMPRATURE = 0x09,
+};
+
+static int nvme_dera_get_device_status(int fd, enum dera_device_status *result)
+{
+ int err = 0;
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = 0xc0,
+ .addr = (__u64)(uintptr_t)NULL,
+ .data_len = 0,
+ .cdw10 = 0,
+ .cdw12 = 0x104,
+ };
+
+ err = nvme_submit_admin_passthru(fd, &cmd, NULL);
+ if (!err && result) {
+ *result = cmd.result;
+ }
+
+ return err;
+}
+
+static int get_status(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_dera_smart_info_log log;
+ enum dera_device_status state = DEVICE_STATUS_FATAL_ERROR;
+ char *desc = "Get the Dera device status";
+ struct nvme_dev *dev;
+ int err;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xc0, sizeof(log), &log);
+ if (err) {
+ goto exit;
+ }
+
+ const char* dev_status[] = {
+ "Normal",
+ "Quick Rebuilding",
+ "Full Rebuilding",
+ "Raw Rebuilding",
+ "Card Read Only",
+ "Fatal Error",
+ "Busy",
+ "Low Level Format",
+ "Firmware Committing",
+ "Over Temperature" };
+
+ const char *volt_status[] = {
+ "Normal",
+ "Initial Low",
+ "Runtime Low",
+ };
+
+ err = nvme_dera_get_device_status(dev_fd(dev), &state);
+ if (!err){
+ if (state > 0 && state < 4){
+ printf("device_status : %s %d%% completed\n", dev_status[state], log.rebuild_percent);
+ }
+ else{
+ printf("device_status : %s\n", dev_status[state]);
+ }
+ }
+ else {
+ goto exit;
+ }
+
+ printf("dev_status_up : %s\n", dev_status[log.dev_status_up]);
+ printf("cap_aged : %s\n", log.cap_aged == 1 ? "True" : "False");
+ printf("cap_aged_ratio : %d%%\n", log.cap_aged_ratio < 100 ? log.cap_aged_ratio : 100);
+ printf("cap_status : %s\n", log.cap_status == 0 ? "Normal" : (log.cap_status == 1 ? "Warning" : "Critical"));
+ printf("cap_voltage : %d mV\n", char4_to_int(log.cap_voltage));
+ printf("nand_erase_err_cnt : %d\n", char4_to_int(log.nand_erase_err_cnt));
+ printf("nand_program_err_cnt : %d\n", char4_to_int(log.nand_program_err_cnt));
+ printf("ddra_1bit_err : %d\n", log.ddra_1bit_err[1] << 8 | log.ddra_1bit_err[0]);
+ printf("ddra_2bit_err : %d\n", log.ddra_2bit_err[1] << 8 | log.ddra_2bit_err[0]);
+ printf("ddrb_1bit_err : %d\n", log.ddrb_1bit_err[1] << 8 | log.ddrb_1bit_err[0]);
+ printf("ddrb_2bit_err : %d\n", log.ddrb_2bit_err[1] << 8 | log.ddrb_2bit_err[0]);
+ printf("ddr_err_bit : %d\n", log.ddr_err_bit);
+ printf("pcie_corr_err : %d\n", log.pcie_corr_err[1] << 8 | log.pcie_corr_err[0]);
+ printf("pcie_uncorr_err : %d\n", log.pcie_uncorr_err[1] << 8 | log.pcie_uncorr_err[0]);
+ printf("pcie_fatal_err : %d\n", log.pcie_fatal_err[1] << 8 | log.pcie_fatal_err[0]);
+ printf("power_level : %d W\n", log.power_level);
+ printf("current_power : %d mW\n", log.current_power[1] << 8 | log.current_power[0]);
+ printf("nand_init_fail : %d\n", log.nand_init_fail[1] << 8 | log.nand_init_fail[0]);
+ printf("fw_loader_version : %.*s\n", 8, log.fw_loader_version);
+ printf("uefi_driver_version : %.*s\n", 8, log.uefi_driver_version);
+
+ if (log.pcie_volt_status < sizeof(volt_status) / sizeof(const char *)){
+ printf("pcie_volt_status : %s\n", volt_status[log.pcie_volt_status]);
+ }
+ else{
+ printf("pcie_volt_status : Unknown\n");
+ }
+
+ printf("current_pcie_volt : %d mV\n", log.current_pcie_volt[1] << 8 | log.current_pcie_volt[0]);
+ printf("init_pcie_volt_low_cnt : %d\n", log.init_pcie_volt_low[1] << 8 | log.init_pcie_volt_low[0]);
+ printf("rt_pcie_volt_low_cnt : %d\n", log.rt_pcie_volt_low[1] << 8 | log.rt_pcie_volt_low[0]);
+ printf("temp_sensor_abnormal_cnt : %d\n", log.temp_sensor_abnormal[1] << 8 | log.temp_sensor_abnormal[0]);
+ printf("nand_read_retry_fail_cnt : %d\n", char4_to_int(log.nand_read_retry_fail_cnt));
+ printf("fw_slot_version : %.*s\n", 8, log.fw_slot_version);
+
+exit:
+ if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
diff --git a/plugins/dera/dera-nvme.h b/plugins/dera/dera-nvme.h
new file mode 100644
index 0000000..a5bb0ae
--- /dev/null
+++ b/plugins/dera/dera-nvme.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/dera/dera-nvme
+
+#if !defined(DERA_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define DERA_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("dera", "Dera vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve Dera SMART Log, show it", get_status, "stat")
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/fdp/fdp.c b/plugins/fdp/fdp.c
new file mode 100644
index 0000000..9fef271
--- /dev/null
+++ b/plugins/fdp/fdp.c
@@ -0,0 +1,537 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#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 "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "fdp.h"
+
+static int fdp_configs(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Get Flexible Data Placement Configurations";
+ const char *egid = "Endurance group identifier";
+ const char *human_readable = "show log in readable format";
+ const char *raw = "use binary output";
+
+ enum nvme_print_flags flags;
+ struct nvme_dev *dev;
+ struct nvme_fdp_config_log hdr;
+ void *log = NULL;
+ int err;
+
+ struct config {
+ __u16 egid;
+ char *output_format;
+ bool human_readable;
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ .egid = 0,
+ .output_format = "normal",
+ .raw_binary = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("endgrp-id", 'e', &cfg.egid, egid),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto out;
+
+ if (cfg.raw_binary)
+ flags = BINARY;
+
+ if (cfg.human_readable)
+ flags |= VERBOSE;
+
+ if (!cfg.egid) {
+ fprintf(stderr, "endurance group identifier required\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = nvme_get_log_fdp_configurations(dev->direct.fd, cfg.egid, 0,
+ sizeof(hdr), &hdr);
+ if (err) {
+ nvme_show_status(errno);
+ goto out;
+ }
+
+ log = malloc(hdr.size);
+ if (!log) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = nvme_get_log_fdp_configurations(dev->direct.fd, cfg.egid, 0,
+ hdr.size, log);
+ if (err) {
+ nvme_show_status(errno);
+ goto out;
+ }
+
+ nvme_show_fdp_configs(log, hdr.size, flags);
+
+out:
+ dev_close(dev);
+ free(log);
+
+ return err;
+}
+
+static int fdp_usage(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Get Flexible Data Placement Reclaim Unit Handle Usage";
+ const char *egid = "Endurance group identifier";
+ const char *raw = "use binary output";
+
+ enum nvme_print_flags flags;
+ struct nvme_dev *dev;
+ struct nvme_fdp_ruhu_log hdr;
+ size_t len;
+ void *log = NULL;
+ int err;
+
+ struct config {
+ __u16 egid;
+ char *output_format;
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ .egid = 0,
+ .output_format = "normal",
+ .raw_binary = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("endgrp-id", 'e', &cfg.egid, egid),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto out;
+
+ if (cfg.raw_binary)
+ flags = BINARY;
+
+ err = nvme_get_log_reclaim_unit_handle_usage(dev->direct.fd, cfg.egid,
+ 0, sizeof(hdr), &hdr);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ len = sizeof(hdr) + le16_to_cpu(hdr.nruh) * sizeof(struct nvme_fdp_ruhu_desc);
+ log = malloc(len);
+ if (!log) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = nvme_get_log_reclaim_unit_handle_usage(dev->direct.fd, cfg.egid,
+ 0, len, log);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ nvme_show_fdp_usage(log, len, flags);
+
+out:
+ dev_close(dev);
+ free(log);
+
+ return err;
+}
+
+static int fdp_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Get Flexible Data Placement Statistics";
+ const char *egid = "Endurance group identifier";
+ const char *raw = "use binary output";
+
+ enum nvme_print_flags flags;
+ struct nvme_dev *dev;
+ struct nvme_fdp_stats_log stats;
+ int err;
+
+ struct config {
+ __u16 egid;
+ char *output_format;
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ .egid = 0,
+ .output_format = "normal",
+ .raw_binary = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("endgrp-id", 'e', &cfg.egid, egid),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto out;
+
+ if (cfg.raw_binary)
+ flags = BINARY;
+
+ memset(&stats, 0x0, sizeof(stats));
+
+ err = nvme_get_log_fdp_stats(dev->direct.fd, cfg.egid, 0, sizeof(stats), &stats);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ nvme_show_fdp_stats(&stats, flags);
+
+out:
+ dev_close(dev);
+
+ return err;
+}
+
+static int fdp_events(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Get Flexible Data Placement Events";
+ const char *egid = "Endurance group identifier";
+ const char *host_events = "Get host events";
+ const char *raw = "use binary output";
+
+ enum nvme_print_flags flags;
+ struct nvme_dev *dev;
+ struct nvme_fdp_events_log events;
+ int err;
+
+ struct config {
+ __u16 egid;
+ bool host_events;
+ char *output_format;
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ .egid = 0,
+ .host_events = false,
+ .output_format = "normal",
+ .raw_binary = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("endgrp-id", 'e', &cfg.egid, egid),
+ OPT_FLAG("host-events", 'E', &cfg.host_events, host_events),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto out;
+
+ if (cfg.raw_binary)
+ flags = BINARY;
+
+ memset(&events, 0x0, sizeof(events));
+
+ err = nvme_get_log_fdp_events(dev->direct.fd, cfg.egid,
+ cfg.host_events, 0, sizeof(events), &events);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ nvme_show_fdp_events(&events, flags);
+
+out:
+ dev_close(dev);
+
+ return err;
+}
+
+static int fdp_status(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Reclaim Unit Handle Status";
+ const char *namespace_id = "Namespace identifier";
+ const char *raw = "use binary output";
+
+ enum nvme_print_flags flags;
+ struct nvme_dev *dev;
+ struct nvme_fdp_ruh_status hdr;
+ size_t len;
+ void *buf = NULL;
+ int err = -1;
+
+ struct config {
+ __u32 namespace_id;
+ char *output_format;
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ .raw_binary = false,
+ };
+
+ 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("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto out;
+
+ if (cfg.raw_binary)
+ flags = BINARY;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto out;
+ }
+ }
+
+ err = nvme_fdp_reclaim_unit_handle_status(dev_fd(dev),
+ cfg.namespace_id, sizeof(hdr), &hdr);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ len = le16_to_cpu(hdr.nruhsd) * sizeof(struct nvme_fdp_ruh_status_desc);
+ buf = malloc(len);
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = nvme_fdp_reclaim_unit_handle_status(dev_fd(dev),
+ cfg.namespace_id, len, buf);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ nvme_show_fdp_ruh_status(buf, len, flags);
+
+out:
+ free(buf);
+ dev_close(dev);
+
+ return err;
+}
+
+static int fdp_update(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Reclaim Unit Handle Update";
+ const char *namespace_id = "Namespace identifier";
+ const char *_pids = "Comma-separated list of placement identifiers to update";
+
+ struct nvme_dev *dev;
+ unsigned short pids[256];
+ __u16 buf[256];
+ int npids;
+ int err = -1;
+
+ struct config {
+ __u32 namespace_id;
+ char *pids;
+ };
+
+ struct config cfg = {
+ .pids = "",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_LIST("pids", 'p', &cfg.pids, _pids),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ npids = argconfig_parse_comma_sep_array_short(cfg.pids, pids, ARRAY_SIZE(pids));
+ if (npids < 0) {
+ perror("could not parse pids");
+ err = -EINVAL;
+ goto out;
+ } else if (npids == 0) {
+ fprintf(stderr, "no placement identifiers set\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto out;
+ }
+ }
+
+ for (unsigned int i = 0; i < npids; i++) {
+ buf[i] = cpu_to_le16(pids[i]);
+ }
+
+ err = nvme_fdp_reclaim_unit_handle_update(dev_fd(dev), cfg.namespace_id, npids, buf);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ printf("update: Success\n");
+
+out:
+ dev_close(dev);
+
+ return err;
+}
+
+static int fdp_set_events(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Enable or disable FDP events";
+ const char *namespace_id = "Namespace identifier";
+ const char *enable = "Enable/disable event";
+ const char *event_types = "Comma-separated list of event types";
+ const char *ph = "Placement Handle";
+
+ struct nvme_dev *dev;
+ int err = -1;
+ unsigned short evts[255];
+ int nev;
+ __u8 buf[255];
+
+ struct config {
+ __u32 namespace_id;
+ __u16 ph;
+ char *event_types;
+ bool enable;
+ };
+
+ struct config cfg = {
+ .enable = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_SHRT("placement-handle", 'p', &cfg.ph, ph),
+ OPT_FLAG("enable", 'e', &cfg.enable, enable),
+ OPT_LIST("event-types", 't', &cfg.event_types, event_types),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ nev = argconfig_parse_comma_sep_array_short(cfg.event_types, evts, ARRAY_SIZE(evts));
+ if (nev < 0) {
+ perror("could not parse event types");
+ err = -EINVAL;
+ goto out;
+ } else if (nev == 0) {
+ fprintf(stderr, "no event types set\n");
+ err = -EINVAL;
+ goto out;
+ } else if (nev > 255) {
+ fprintf(stderr, "too many event types (max 255)\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ if (errno != ENOTTY) {
+ fprintf(stderr, "get-namespace-id: %s\n", nvme_strerror(errno));
+ goto out;
+ }
+
+ cfg.namespace_id = NVME_NSID_ALL;
+ }
+ }
+
+ for (unsigned int i = 0; i < nev; i++) {
+ buf[i] = (__u8)evts[i];
+ }
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = NVME_FEAT_FID_FDP_EVENTS,
+ .nsid = cfg.namespace_id,
+ .cdw11 = (nev << 16) | cfg.ph,
+ .cdw12 = cfg.enable ? 0x1 : 0x0,
+ .data_len = sizeof(buf),
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+
+ err = nvme_set_features(&args);
+ if (err) {
+ nvme_show_status(err);
+ goto out;
+ }
+
+ printf("set-events: Success\n");
+
+out:
+ dev_close(dev);
+
+ return err;
+}
diff --git a/plugins/fdp/fdp.h b/plugins/fdp/fdp.h
new file mode 100644
index 0000000..f162b32
--- /dev/null
+++ b/plugins/fdp/fdp.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/fdp/fdp
+
+#if !defined(FDP_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define FDP_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("fdp", "Manage Flexible Data Placement enabled devices", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("configs", "List configurations", fdp_configs)
+ ENTRY("usage", "Show reclaim unit handle usage", fdp_usage)
+ ENTRY("stats", "Show statistics", fdp_stats)
+ ENTRY("events", "List events affecting reclaim units and media usage", fdp_events)
+ ENTRY("status", "Show reclaim unit handle status", fdp_status)
+ ENTRY("update", "Update a reclaim unit handle", fdp_update)
+ ENTRY("set-events", "Enabled or disable events", fdp_set_events)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c
new file mode 100644
index 0000000..572086c
--- /dev/null
+++ b/plugins/huawei/huawei-nvme.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017-2019 Huawei Corporation or its affiliates.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Author: Zou Ming<zouming.zouming@huawei.com>,
+ * Yang Feng <philip.yang@huawei.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+
+#include "util/suffix.h"
+
+#define CREATE_CMD
+#include "huawei-nvme.h"
+
+#define HW_SSD_PCI_VENDOR_ID 0x19E5
+#define ARRAY_NAME_LEN 80
+#define NS_NAME_LEN 40
+
+#define MIN_ARRAY_NAME_LEN 16
+#define MIN_NS_NAME_LEN 16
+
+struct huawei_list_item {
+ char node[1024];
+ struct nvme_id_ctrl ctrl;
+ unsigned nsid;
+ struct nvme_id_ns ns;
+ unsigned block;
+ char ns_name[NS_NAME_LEN];
+ char array_name[ARRAY_NAME_LEN];
+ bool huawei_device;
+};
+
+struct huawei_list_element_len {
+ unsigned int node;
+ unsigned int ns_name;
+ unsigned int nguid;
+ unsigned int ns_id;
+ unsigned int usage;
+ unsigned int array_name;
+};
+
+static int huawei_get_nvme_info(int fd, struct huawei_list_item *item, const char *node)
+{
+ int err;
+ int len;
+ struct stat nvme_stat_info;
+
+ memset(item, 0, sizeof(*item));
+
+ err = nvme_identify_ctrl(fd, &item->ctrl);
+ if (err)
+ return err;
+
+ /*identify huawei device*/
+ if (strstr(item->ctrl.mn, "Huawei") == NULL &&
+ le16_to_cpu(item->ctrl.vid) != HW_SSD_PCI_VENDOR_ID) {
+ item->huawei_device = false;
+ return 0;
+ }
+
+ item->huawei_device = true;
+ err = nvme_get_nsid(fd, &item->nsid);
+ err = nvme_identify_ns(fd, item->nsid, &item->ns);
+ if (err)
+ return err;
+
+ err = fstat(fd, &nvme_stat_info);
+ if (err < 0)
+ return err;
+
+ strncpy(item->node, node, sizeof(item->node));
+ item->node[sizeof(item->node) - 1] = '\0';
+ item->block = S_ISBLK(nvme_stat_info.st_mode);
+
+ if (item->ns.vs[0] == 0) {
+
+ len = snprintf(item->ns_name, NS_NAME_LEN, "%s", "----");
+ if (len < 0)
+ return -EINVAL;
+ }
+ else {
+ memcpy(item->ns_name, item->ns.vs, NS_NAME_LEN);
+ item->ns_name[NS_NAME_LEN - 1] = '\0';
+ }
+
+ if (item->ctrl.vs[0] == 0) {
+
+ len = snprintf(item->array_name, ARRAY_NAME_LEN, "%s", "----");
+ if (len < 0)
+ return -EINVAL;
+ }
+ else {
+ memcpy(item->array_name, item->ctrl.vs, ARRAY_NAME_LEN);
+ item->array_name[ARRAY_NAME_LEN - 1] = '\0';
+ }
+ return 0;
+}
+
+static void format(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--;
+ }
+}
+
+static void huawei_json_print_list_items(struct huawei_list_item *list_items,
+ unsigned len)
+{
+ struct json_object *root;
+ struct json_object *devices;
+ struct json_object *device_attrs;
+ char formatter[128] = { 0 };
+ int index, i = 0;
+
+ root = json_create_object();
+ devices = json_create_array();
+ for (i = 0; i < len; i++) {
+ device_attrs = json_create_object();
+
+ json_object_add_value_string(device_attrs,
+ "DevicePath",
+ list_items[i].node);
+
+ if (sscanf(list_items[i].node, "/dev/nvme%d", &index) == 1)
+ json_object_add_value_int(device_attrs,
+ "Index",
+ index);
+
+ format(formatter, sizeof(formatter),
+ list_items[i].ns_name,
+ sizeof(list_items[i].ns_name));
+
+ json_object_add_value_string(device_attrs,
+ "NS Name",
+ formatter);
+
+ format(formatter, sizeof(formatter),
+ list_items[i].array_name,
+ sizeof(list_items[i].array_name));
+
+ json_object_add_value_string(device_attrs,
+ "Array Name",
+ formatter);
+
+ json_array_add_value_object(devices, device_attrs);
+ }
+ json_object_add_value_array(root, "Devices", devices);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static void huawei_print_list_head(struct huawei_list_element_len element_len)
+{
+ char dash[128];
+ int i;
+
+ for (i = 0; i < 128; i++)
+ dash[i] = '-';
+ dash[127] = '\0';
+
+ printf("%-*.*s %-*.*s %-*.*s %-*.*s %-*.*s %-*.*s\n",
+ element_len.node, element_len.node, "Node",
+ element_len.ns_name, element_len.ns_name, "NS Name",
+ element_len.nguid, element_len.nguid, "Nguid",
+ element_len.ns_id, element_len.ns_id, "NS ID",
+ element_len.usage, element_len.usage, "Usage",
+ element_len.array_name, element_len.array_name, "Array Name");
+
+ printf("%-.*s %-.*s %-.*s %-.*s %-.*s %-.*s\n",
+ element_len.node, dash, element_len.ns_name, dash,
+ element_len.nguid, dash, element_len.ns_id, dash,
+ element_len.usage, dash, element_len.array_name, dash);
+}
+
+static void huawei_print_list_item(struct huawei_list_item *list_item,
+ struct huawei_list_element_len element_len)
+{
+ __u8 lba_index;
+ nvme_id_ns_flbas_to_lbaf_inuse(list_item->ns.flbas, &lba_index);
+ unsigned long long int lba = 1ULL << list_item->ns.lbaf[lba_index].ds;
+ double nsze = le64_to_cpu(list_item->ns.nsze) * lba;
+ double nuse = le64_to_cpu(list_item->ns.nuse) * lba;
+
+ const char *s_suffix = suffix_si_get(&nsze);
+ const char *u_suffix = suffix_si_get(&nuse);
+
+ char usage[128];
+ char nguid_buf[2 * sizeof(list_item->ns.nguid) + 1];
+ char *nguid = nguid_buf;
+ int i;
+
+ sprintf(usage,"%6.2f %2sB / %6.2f %2sB", nuse, u_suffix,
+ nsze, s_suffix);
+
+ memset(nguid, 0, sizeof(nguid_buf));
+ for (i = 0; i < sizeof(list_item->ns.nguid); i++)
+ nguid += sprintf(nguid, "%02x", list_item->ns.nguid[i]);
+
+ printf("%-*.*s %-*.*s %-*.*s %-*d %-*.*s %-*.*s\n",
+ element_len.node, element_len.node, list_item->node,
+ element_len.ns_name, element_len.ns_name, list_item->ns_name,
+ element_len.nguid, element_len.nguid, nguid_buf,
+ element_len.ns_id, list_item->nsid,
+ element_len.usage, element_len.usage, usage,
+ element_len.array_name, element_len.array_name,
+ list_item->array_name);
+
+}
+
+static unsigned int choose_len(unsigned int old_len, unsigned int cur_len, unsigned int default_len)
+{
+ unsigned int temp_len;
+
+ temp_len = (cur_len > default_len) ? cur_len : default_len;
+ if (temp_len > old_len)
+ {
+ return temp_len;
+ }
+ return old_len;
+}
+
+static unsigned int huawei_get_ns_len(struct huawei_list_item *list_items, unsigned len, unsigned default_len)
+{
+ int i;
+ unsigned int min_len = default_len;
+
+ for (i = 0 ; i < len ; i++)
+ min_len = choose_len(min_len , strlen(list_items->ns_name), default_len);
+
+ return min_len;
+}
+
+static int huawei_get_array_len(struct huawei_list_item *list_items, unsigned len, unsigned default_len)
+{
+ int i;
+ int min_len = default_len;
+
+ for (i = 0 ; i < len ; i++)
+ min_len = choose_len(min_len , strlen(list_items->array_name), default_len);
+
+ return min_len;
+}
+
+static void huawei_print_list_items(struct huawei_list_item *list_items, unsigned len)
+{
+ unsigned i;
+ struct huawei_list_element_len element_len;
+
+ element_len.node = 16;
+ element_len.nguid = 2 * sizeof(list_items->ns.nguid) + 1;
+ element_len.ns_id = 9;
+ element_len.usage = 26;
+ element_len.ns_name = huawei_get_ns_len(list_items, len, MIN_NS_NAME_LEN);
+ element_len.array_name = huawei_get_array_len(list_items, len, MIN_ARRAY_NAME_LEN);
+
+ huawei_print_list_head(element_len);
+
+ for (i = 0 ; i < len ; i++)
+ huawei_print_list_item(&list_items[i], element_len);
+}
+
+static int huawei_list(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char path[264];
+ struct dirent **devices;
+ struct huawei_list_item *list_items;
+ unsigned int i, n, ret;
+ unsigned int huawei_num = 0;
+ int fmt;
+ const char *desc = "Retrieve basic information for the given huawei device";
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt != JSON && fmt != NORMAL)
+ return -EINVAL;
+
+ n = scandir("/dev", &devices, nvme_namespace_filter, alphasort);
+ if (n <= 0)
+ return n;
+
+ list_items = calloc(n, sizeof(*list_items));
+ if (!list_items) {
+ fprintf(stderr, "can not allocate controller list payload\n");
+ ret = ENOMEM;
+ goto out_free_devices;
+ }
+
+ for (i = 0; i < n; i++) {
+ int fd;
+
+ snprintf(path, sizeof(path), "/dev/%s", devices[i]->d_name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Cannot open device %s: %s\n",
+ path, strerror(errno));
+ continue;
+ }
+ ret = huawei_get_nvme_info(fd, &list_items[huawei_num], path);
+ if (ret) {
+ close(fd);
+ goto out_free_list_items;
+ }
+ if (list_items[huawei_num].huawei_device == true) {
+ huawei_num++;
+ }
+ close(fd);
+ }
+
+ if (huawei_num > 0){
+ if (fmt == JSON)
+ huawei_json_print_list_items(list_items, huawei_num);
+ else
+ huawei_print_list_items(list_items, huawei_num);
+ }
+out_free_list_items:
+ free(list_items);
+out_free_devices:
+ for (i = 0; i < n; i++)
+ free(devices[i]);
+ free(devices);
+
+ return ret;
+}
+
+static void huawei_do_id_ctrl(__u8 *vs, struct json_object *root)
+{
+ char array_name[ARRAY_NAME_LEN + 1] = {0};
+
+ memcpy(array_name, vs, ARRAY_NAME_LEN);
+ if (root)
+ json_object_add_value_string(root, "array name", strlen(array_name) > 1 ? array_name : "NULL");
+ else
+ printf("array name : %s\n", strlen(array_name) > 1 ? array_name : "NULL");
+}
+
+static int huawei_id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return __id_ctrl(argc, argv, cmd, plugin, huawei_do_id_ctrl);
+}
diff --git a/plugins/huawei/huawei-nvme.h b/plugins/huawei/huawei-nvme.h
new file mode 100644
index 0000000..f49e6fd
--- /dev/null
+++ b/plugins/huawei/huawei-nvme.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/huawei/huawei-nvme
+
+#if !defined(HUAWEI_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define HUAWEI_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("huawei", "Huawei vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("list", "List all Huawei NVMe devices and namespaces on machine", huawei_list)
+ ENTRY("id-ctrl", "Huawei identify controller", huawei_id_ctrl)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/innogrit/innogrit-nvme.c b/plugins/innogrit/innogrit-nvme.c
new file mode 100644
index 0000000..b5d40dd
--- /dev/null
+++ b/plugins/innogrit/innogrit-nvme.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "nvme-print.h"
+#include "typedef.h"
+
+#define CREATE_CMD
+#include "innogrit-nvme.h"
+
+static int innogrit_smart_log_additional(int argc, char **argv,
+ struct command *command,
+ struct plugin *plugin)
+{
+ struct nvme_smart_log smart_log = { 0 };
+ struct vsc_smart_log *pvsc_smart = (struct vsc_smart_log *)smart_log.rsvd232;
+ const char *desc = "Retrieve additional SMART log for the given device ";
+ const char *namespace = "(optional) desired namespace";
+ struct nvme_dev *dev;
+ int err, i, iindex;
+
+ struct config {
+ __u32 namespace_id;
+ };
+
+ struct config cfg = {
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ nvme_get_log_smart(dev_fd(dev), cfg.namespace_id, false, &smart_log);
+ nvme_show_smart_log(&smart_log, cfg.namespace_id, dev->name, NORMAL);
+
+ printf("DW0[0-1] Defect Cnt : %u\n", pvsc_smart->defect_cnt);
+ printf("DW0[2-3] Slc Spb Cnt : %u\n", pvsc_smart->slc_spb_cnt);
+ printf("DW1 Slc Total Ec Cnt : %u\n", pvsc_smart->slc_total_ec_cnt);
+ printf("DW2 Slc Max Ec Cnt : %u\n", pvsc_smart->slc_max_ec_cnt);
+ printf("DW3 Slc Min Ec Cnt : %u\n", pvsc_smart->slc_min_ec_cnt);
+ printf("DW4 Slc Avg Ec Cnt : %u\n", pvsc_smart->slc_avg_ec_cnt);
+ printf("DW5 Total Ec Cnt : %u\n", pvsc_smart->total_ec_cnt);
+ printf("DW6 Max Ec Cnt : %u\n", pvsc_smart->max_ec_cnt);
+ printf("DW7 Min Ec Cnt : %u\n", pvsc_smart->min_ec_cnt);
+ printf("DW8 Avg Ec Cnt : %u\n", pvsc_smart->avg_ec_cnt);
+ printf("DW9 Mrd Rr Good Cnt : %u\n", pvsc_smart->mrd_rr_good_cnt);
+ printf("DW10 Ard Rr Good Cnt : %u\n", pvsc_smart->ard_rr_good_cnt);
+ printf("DW11 Preset Cnt : %u\n", pvsc_smart->preset_cnt);
+ printf("DW12 Nvme Reset Cnt : %u\n", pvsc_smart->nvme_reset_cnt);
+ printf("DW13 Low Pwr Cnt : %u\n", pvsc_smart->low_pwr_cnt);
+ printf("DW14 Wa : %u\n", pvsc_smart->wa);
+ printf("DW15 Ps3 Entry Cnt : %u\n", pvsc_smart->ps3_entry_cnt);
+ printf("DW16[0] highest_temp[0] : %u\n", pvsc_smart->highest_temp[0]);
+ printf("DW16[1] highest_temp[1] : %u\n", pvsc_smart->highest_temp[1]);
+ printf("DW16[2] highest_temp[2] : %u\n", pvsc_smart->highest_temp[2]);
+ printf("DW16[3] highest_temp[3] : %u\n", pvsc_smart->highest_temp[3]);
+ printf("DW17 weight_ec : %u\n", pvsc_smart->weight_ec);
+ printf("DW18 slc_cap_mb : %u\n", pvsc_smart->slc_cap_mb);
+ printf("DW19-20 nand_page_write_cnt : %llu\n", pvsc_smart->nand_page_write_cnt);
+ printf("DW21 program_error_cnt : %u\n", pvsc_smart->program_error_cnt);
+ printf("DW22 erase_error_cnt : %u\n", pvsc_smart->erase_error_cnt);
+ printf("DW23[0] flash_type : %u\n", pvsc_smart->flash_type);
+ printf("DW24 hs_crc_err_cnt : %u\n", pvsc_smart->hs_crc_err_cnt);
+ printf("DW25 ddr_ecc_err_cnt : %u\n", pvsc_smart->ddr_ecc_err_cnt);
+ iindex = 26;
+ for (i = 0; i < (sizeof(pvsc_smart->reserved3)/4); i++) {
+ if (pvsc_smart->reserved3[i] != 0)
+ printf("DW%-37d : %u\n", iindex, pvsc_smart->reserved3[i]);
+ iindex++;
+ }
+
+ return 0;
+}
+
+static int sort_eventlog_fn(const void *a, const void *b)
+{
+ const struct eventlog_addindex *l = a;
+ const struct eventlog_addindex *r = b;
+ int rc;
+
+ if (l->ms > r->ms) {
+ rc = 1;
+ } else if (l->ms < r->ms) {
+ rc = -1;
+ } else {
+ if (l->iindex < r->iindex)
+ rc = -1;
+ else
+ rc = 1;
+ }
+
+ return rc;
+}
+
+static void sort_eventlog(struct eventlog *data16ksrc, unsigned int icount)
+{
+ struct eventlog_addindex peventlogadd[512];
+ unsigned int i;
+
+ for (i = 0; i < icount; i++) {
+ memcpy(&peventlogadd[i], &data16ksrc[i], sizeof(struct eventlog));
+ peventlogadd[i].iindex = i;
+ }
+
+ qsort(peventlogadd, icount, sizeof(struct eventlog_addindex), sort_eventlog_fn);
+
+ for (i = 0; i < icount; i++)
+ memcpy(&data16ksrc[i], &peventlogadd[i], sizeof(struct eventlog));
+}
+
+static unsigned char setfilecontent(char *filenamea, unsigned char *buffer,
+ unsigned int buffersize)
+{
+ FILE *fp = NULL;
+ int rc;
+
+ if (buffersize == 0)
+ return true;
+ fp = fopen(filenamea, "a+");
+ rc = fwrite(buffer, 1, buffersize, fp);
+ fclose(fp);
+ if (rc != buffersize)
+ return false;
+ return true;
+}
+
+static int nvme_vucmd(int fd, unsigned char opcode, unsigned int cdw12,
+ unsigned int cdw13, unsigned int cdw14,
+ unsigned int cdw15, char *data, int data_len)
+{
+ struct nvme_passthru_cmd cmd;
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = opcode;
+ cmd.cdw12 = cdw12;
+ cmd.cdw13 = cdw13;
+ cmd.cdw14 = cdw14;
+ cmd.cdw15 = cdw15;
+ cmd.nsid = 0;
+ cmd.addr = (__u64)(__u64)(uintptr_t)data;
+ cmd.data_len = data_len;
+ return nvme_submit_admin_passthru(fd, &cmd, NULL);
+}
+
+static int innogrit_vsc_geteventlog(int argc, char **argv,
+ struct command *command,
+ struct plugin *plugin)
+{
+ time_t timep;
+ struct tm *logtime;
+ int icount, ioffset16k, iblock;
+ char currentdir[128], filename[512];
+ unsigned char data[4096], data16k[SIZE_16K], zerob[32];
+ unsigned int *pcheckdata;
+ unsigned int isize, icheck_stopvalue, iend;
+ unsigned char bSortLog = false, bget_nextlog = true;
+ struct evlg_flush_hdr *pevlog = (struct evlg_flush_hdr *)data;
+ const char *desc = "Recrieve event log for the given device ";
+ const char *clean_opt = "(optional) 1 for clean event log";
+ struct nvme_dev *dev;
+ int ret = -1;
+
+ struct config {
+ __u32 clean_flg;
+ };
+
+ struct config cfg = {
+ .clean_flg = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("clean_flg", 'c', &cfg.clean_flg, clean_opt),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+
+ if (getcwd(currentdir, 128) == NULL)
+ return -1;
+
+ time(&timep);
+ logtime = localtime(&timep);
+ sprintf(filename, "%s/eventlog_%02d%02d-%02d%02d%02d.elog", currentdir, logtime->tm_mon+1,
+ logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec);
+
+ iblock = 0;
+ ioffset16k = 0;
+ memset(data16k, 0, SIZE_16K);
+ memset(zerob, 0, 32);
+
+ icount = 0;
+ while (bget_nextlog) {
+ if (icount % 100 == 0) {
+ printf("\rWait for Dump EventLog " XCLEAN_LINE);
+ fflush(stdout);
+ icount = 0;
+ } else if (icount % 5 == 0) {
+ printf(".");
+ fflush(stdout);
+ }
+ icount++;
+
+ memset(data, 0, 4096);
+ ret = nvme_vucmd(dev_fd(dev), NVME_VSC_GET_EVENT_LOG, 0, 0,
+ (SRB_SIGNATURE >> 32),
+ (SRB_SIGNATURE & 0xFFFFFFFF),
+ (char *)data, 4096);
+ if (ret == -1)
+ return ret;
+
+ pcheckdata = (unsigned int *)&data[4096 - 32];
+ icheck_stopvalue = pcheckdata[1];
+
+ if (icheck_stopvalue == 0xFFFFFFFF) {
+ isize = pcheckdata[0];
+ if (isize == 0) {
+ /* Finish Log */
+ bget_nextlog = false;
+ } else if (bSortLog) {
+ /* No Full 4K Package */
+ for (iend = 0; iend < isize - 32; iend += sizeof(struct eventlog)) {
+ if (memcmp(&data[iend], zerob, sizeof(struct eventlog)) != 0) {
+ memcpy(&data16k[ioffset16k], &data[iend], sizeof(struct eventlog));
+ ioffset16k += sizeof(struct eventlog);
+ }
+ }
+ } else {
+ setfilecontent(filename, data, isize);
+ }
+ } else {
+ /* Full 4K Package */
+ if ((pevlog->signature == EVLOG_SIG) && (pevlog->log_type == 1))
+ bSortLog = true;
+
+ if (bSortLog) {
+ for (iend = 0; iend < SIZE_4K; iend += sizeof(struct eventlog)) {
+ if (memcmp(&data[iend], zerob, sizeof(struct eventlog)) != 0) {
+ memcpy(&data16k[ioffset16k], &data[iend], sizeof(struct eventlog));
+ ioffset16k += sizeof(struct eventlog);
+ }
+ }
+
+ iblock++;
+ if (iblock == 4) {
+ sort_eventlog((struct eventlog *)(data16k + sizeof(struct evlg_flush_hdr)),
+ (ioffset16k - sizeof(struct evlg_flush_hdr))/sizeof(struct eventlog));
+ setfilecontent(filename, data16k, ioffset16k);
+ ioffset16k = 0;
+ iblock = 0;
+ memset(data16k, 0, SIZE_16K);
+ }
+ } else {
+ setfilecontent(filename, data, SIZE_4K);
+ }
+
+ }
+ }
+
+ if (bSortLog) {
+ if (ioffset16k > 0) {
+ sort_eventlog((struct eventlog *)(data16k + sizeof(struct evlg_flush_hdr)),
+ (ioffset16k - sizeof(struct evlg_flush_hdr))/sizeof(struct eventlog));
+ setfilecontent(filename, data16k, ioffset16k);
+ }
+ }
+
+ printf("\r" XCLEAN_LINE "Dump eventLog finish to %s\n", filename);
+ chmod(filename, 0666);
+
+ if (cfg.clean_flg == 1) {
+ printf("Clean eventlog\n");
+ nvme_vucmd(dev_fd(dev), NVME_VSC_CLEAN_EVENT_LOG, 0, 0,
+ (SRB_SIGNATURE >> 32),
+ (SRB_SIGNATURE & 0xFFFFFFFF), (char *)NULL, 0);
+ }
+
+ dev_close(dev);
+
+ return ret;
+}
+
+static int innogrit_vsc_getcdump(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ time_t timep;
+ struct tm *logtime;
+ char currentdir[128], filename[512], fname[128];
+ unsigned int itotal, icur;
+ unsigned char data[4096];
+ struct cdumpinfo cdumpinfo;
+ unsigned char busevsc = false;
+ unsigned int ipackcount, ipackindex;
+ char fwvera[32];
+ const char *desc = "Recrieve cdump data for the given device ";
+ struct nvme_dev *dev;
+ int ret = -1;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ if (getcwd(currentdir, 128) == NULL)
+ return -1;
+
+ time(&timep);
+ logtime = localtime(&timep);
+
+ ipackindex = 0;
+ memset(data, 0, 4096);
+ if (nvme_vucmd(dev_fd(dev), NVME_VSC_GET, VSC_FN_GET_CDUMP, 0x00,
+ (SRB_SIGNATURE >> 32), (SRB_SIGNATURE & 0xFFFFFFFF),
+ (char *)data, 4096) == 0) {
+ memcpy(&cdumpinfo, &data[3072], sizeof(cdumpinfo));
+ if (cdumpinfo.sig == 0x5a5b5c5d) {
+ busevsc = true;
+ ipackcount = cdumpinfo.ipackcount;
+ if (ipackcount == 0) {
+ itotal = 0;
+ } else {
+ itotal = cdumpinfo.cdumppack[ipackindex].ilenth;
+ memset(fwvera, 0, sizeof(fwvera));
+ memcpy(fwvera, cdumpinfo.cdumppack[ipackindex].fwver, 8);
+ sprintf(fname, "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", logtime->tm_mon+1,
+ logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec,
+ ipackindex, fwvera);
+ sprintf(filename, "%s/%s", currentdir, fname);
+ }
+ }
+ }
+
+ if (busevsc == false) {
+ memset(data, 0, 4096);
+ ret = nvme_get_nsid_log(dev_fd(dev), true, 0x07,
+ NVME_NSID_ALL,
+ 4096, data);
+ if (ret != 0)
+ return ret;
+
+ ipackcount = 1;
+ memcpy(&itotal, &data[4092], 4);
+ sprintf(fname, "cdump_%02d%02d-%02d%02d%02d.cdp", logtime->tm_mon+1, logtime->tm_mday,
+ logtime->tm_hour, logtime->tm_min, logtime->tm_sec);
+ sprintf(filename, "%s/%s", currentdir, fname);
+ }
+
+ if (itotal == 0) {
+ printf("no cdump data\n");
+ return 0;
+ }
+
+ while (ipackindex < ipackcount) {
+ memset(data, 0, 4096);
+ strcpy((char *)data, "cdumpstart");
+ setfilecontent(filename, data, strlen((char *)data));
+ for (icur = 0; icur < itotal; icur += 4096) {
+ memset(data, 0, 4096);
+ if (busevsc)
+ ret = nvme_vucmd(dev_fd(dev), NVME_VSC_GET,
+ VSC_FN_GET_CDUMP, 0x00,
+ (SRB_SIGNATURE >> 32),
+ (SRB_SIGNATURE & 0xFFFFFFFF),
+ (char *)data, 4096);
+ else
+ ret = nvme_get_nsid_log(dev_fd(dev), true,
+ 0x07,
+ NVME_NSID_ALL, 4096, data);
+ if (ret != 0)
+ return ret;
+
+ setfilecontent(filename, data, 4096);
+
+ printf("\rWait for dump data %d%%" XCLEAN_LINE, ((icur+4096) * 100/itotal));
+ }
+ memset(data, 0, 4096);
+ strcpy((char *)data, "cdumpend");
+ setfilecontent(filename, data, strlen((char *)data));
+ printf("\r%s\n", fname);
+ ipackindex++;
+ if (ipackindex != ipackcount) {
+ memset(data, 0, 4096);
+ if (busevsc)
+ ret = nvme_vucmd(dev_fd(dev), NVME_VSC_GET,
+ VSC_FN_GET_CDUMP, 0x00,
+ (SRB_SIGNATURE >> 32),
+ (SRB_SIGNATURE & 0xFFFFFFFF),
+ (char *)data, 4096);
+ else
+ ret = nvme_get_nsid_log(dev_fd(dev), true,
+ 0x07,
+ NVME_NSID_ALL, 4096,
+ data);
+ if (ret != 0)
+ return ret;
+
+ itotal = cdumpinfo.cdumppack[ipackindex].ilenth;
+ memset(fwvera, 0, sizeof(fwvera));
+ memcpy(fwvera, cdumpinfo.cdumppack[ipackindex].fwver, 8);
+ sprintf(fname, "cdump_%02d%02d-%02d%02d%02d_%d_%s.cdp", logtime->tm_mon+1,
+ logtime->tm_mday, logtime->tm_hour, logtime->tm_min, logtime->tm_sec,
+ ipackindex, fwvera);
+ sprintf(filename, "%s/%s", currentdir, fname);
+ }
+
+ }
+
+ printf("\n");
+ dev_close(dev);
+ return ret;
+}
diff --git a/plugins/innogrit/innogrit-nvme.h b/plugins/innogrit/innogrit-nvme.h
new file mode 100644
index 0000000..2de0502
--- /dev/null
+++ b/plugins/innogrit/innogrit-nvme.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/innogrit/innogrit-nvme
+
+#if !defined(INNOGRIT_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define INNOGRIT_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("innogrit", "innogrit vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve innogrit SMART Log, show it", innogrit_smart_log_additional)
+ ENTRY("get-eventlog", "get event log", innogrit_vsc_geteventlog)
+ ENTRY("get-cdump", "get cdump data", innogrit_vsc_getcdump)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/innogrit/typedef.h b/plugins/innogrit/typedef.h
new file mode 100644
index 0000000..a97a008
--- /dev/null
+++ b/plugins/innogrit/typedef.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#define SIZE_4K 4096
+#define SIZE_16K 16384
+
+#define NVME_VSC_GET_EVENT_LOG 0xC2
+#define NVME_VSC_CLEAN_EVENT_LOG 0xD8
+#define NVME_VSC_GET 0xE6
+#define VSC_FN_GET_CDUMP 0x08
+#define EVLOG_SIG 0x65766C67
+#define SRB_SIGNATURE 0x544952474F4E4E49ULL
+#define XCLEAN_LINE "\033[K"
+
+struct evlg_flush_hdr {
+ unsigned int signature;
+ unsigned int fw_ver[2];
+ unsigned int fw_type : 8;
+ unsigned int log_type : 8;
+ unsigned int project : 16;
+ unsigned int trace_cnt;
+ unsigned int sout_crc;
+ unsigned int reserved[2];
+};
+
+struct eventlog {
+ unsigned int ms;
+ unsigned int param[7];
+};
+
+struct eventlog_addindex {
+ unsigned int ms;
+ unsigned int param[7];
+ unsigned int iindex;
+};
+
+#pragma pack(push)
+#pragma pack(1)
+struct vsc_smart_log {
+ unsigned short defect_cnt;
+ unsigned short slc_spb_cnt;
+ unsigned int slc_total_ec_cnt;
+ unsigned int slc_max_ec_cnt;
+ unsigned int slc_min_ec_cnt;
+ unsigned int slc_avg_ec_cnt;
+ unsigned int total_ec_cnt;
+ unsigned int max_ec_cnt;
+ unsigned int min_ec_cnt;
+ unsigned int avg_ec_cnt;
+ unsigned int mrd_rr_good_cnt;
+ unsigned int ard_rr_good_cnt;
+ unsigned int preset_cnt;
+ unsigned int nvme_reset_cnt;
+ unsigned int low_pwr_cnt;
+ unsigned int wa;
+ unsigned int ps3_entry_cnt;
+ u_char highest_temp[4];
+ unsigned int weight_ec;
+ unsigned int slc_cap_mb;
+ unsigned long long nand_page_write_cnt;
+ unsigned int program_error_cnt;
+ unsigned int erase_error_cnt;
+ u_char flash_type;
+ u_char reserved2[3];
+ unsigned int hs_crc_err_cnt;
+ unsigned int ddr_ecc_err_cnt;
+ unsigned int reserved3[44];
+};
+#pragma pack(pop)
+
+struct cdump_pack {
+ unsigned int ilenth;
+ char fwver[8];
+};
+
+struct cdumpinfo {
+ unsigned int sig;
+ unsigned int ipackcount;
+ struct cdump_pack cdumppack[32];
+};
diff --git a/plugins/inspur/inspur-nvme.c b/plugins/inspur/inspur-nvme.c
new file mode 100644
index 0000000..9d7bb4d
--- /dev/null
+++ b/plugins/inspur/inspur-nvme.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "nvme-print.h"
+#include "util/suffix.h"
+
+#define CREATE_CMD
+#include "inspur-nvme.h"
+#include "inspur-utils.h"
+
+void show_r1_vendor_log(r1_cli_vendor_log_t *vendorlog)
+{
+ int i = 0;
+
+ if (vendorlog->device_state == 0) {
+ printf("device_state : [healthy]\n");
+ } else {
+ printf("device_state : [warning]\n");
+ }
+
+ printf("commit id : %s\n", vendorlog->commit_id);
+ printf("mcu data id(mcu) : 0x%x\n", le32_to_cpu(vendorlog->mcu_data_id));
+ printf("power_info(mcu) : %u mW\n", le32_to_cpu(vendorlog->power_info));
+ printf("voltage_info(mcu) : %u mV\n", le32_to_cpu(vendorlog->voltage_info));
+ printf("current_info(mcu) : %u mA\n", le32_to_cpu(vendorlog->current_info));
+ printf("history max_power(mcu) : %u mW\n", le32_to_cpu(vendorlog->max_power));
+ printf("disk_max_temper(mcu) : %d C\n", le32_to_cpu(vendorlog->disk_max_temper) - 273);
+ printf("disk_overtemper_cout(mcu) : %u\n", le32_to_cpu(vendorlog->disk_overtemper_cout));
+ printf("ctrl_max_temper(mcu) : %d C\n", le32_to_cpu(vendorlog->ctrl_max_temper) - 273);
+ printf("ctrl_overtemper_cout(mcu) : %u\n", le32_to_cpu(vendorlog->ctrl_overtemper_cout));
+ printf("nand_max_temper(mcu) : %d C\n", le32_to_cpu(vendorlog->nand_max_temper) - 273);
+ printf("nand_overtemper_cout(mcu) : %u\n", le32_to_cpu(vendorlog->nand_overtemper_cout));
+
+ for (i = 0; i < 4; i++) {
+ printf("temperature[%d](mcu) : %d C\n", i, le32_to_cpu(vendorlog->current_temp[i]) - 273);
+ }
+
+ printf("CAP Time from 32v to 27v(mcu) : %u ms\n", le32_to_cpu(vendorlog->cap_transtime.cap_trans_time1));
+ printf("CAP Time from 27v to 10v(mcu) : %u ms\n", le32_to_cpu(vendorlog->cap_transtime.cap_trans_time2));
+ printf("cap_health_state(mcu) : %u\n", le32_to_cpu(vendorlog->cap_health_state));
+ printf("warning bit(mcu) : 0x%x%08x\n", le32_to_cpu(vendorlog->detail_warning[1]),
+ le32_to_cpu(vendorlog->detail_warning[0]));
+ printf("-->high_format_fail : %x\n", vendorlog->detail_warning_bit.high_format_fail);
+ printf("-->low_format_fail : %x\n", vendorlog->detail_warning_bit.low_format_fail);
+ printf("-->current sensor : %x\n", vendorlog->detail_warning_bit.self_test_fail1);
+ printf("-->nand temp sensor : %x\n", vendorlog->detail_warning_bit.self_test_fail2);
+ printf("-->board temp sensor : %x\n", vendorlog->detail_warning_bit.self_test_fail3);
+ printf("-->cntl temp sensor : %x\n", vendorlog->detail_warning_bit.self_test_fail4);
+ printf("-->cap_timer_test_fail : %x\n", vendorlog->detail_warning_bit.capacitance_test_fail);
+ printf("-->readOnly_after_rebuild : %x\n", vendorlog->detail_warning_bit.readOnly_after_rebuild);
+ printf("-->firmware_loss : %x\n", vendorlog->detail_warning_bit.firmware_loss);
+ printf("-->cap_self_test : %x\n", vendorlog->detail_warning_bit.cap_unsupply);
+ printf("-->spare_space_warning : %x\n", vendorlog->detail_warning_bit.spare_space_warning);
+ printf("-->lifetime_warning : %x\n", vendorlog->detail_warning_bit.lifetime_warning);
+ printf("-->temp_high_warning : %x\n", vendorlog->detail_warning_bit.temp_high_warning);
+ printf("-->temp_low_warning : %x\n", vendorlog->detail_warning_bit.temp_low_warning);
+ printf("-->mcu_disable(mcu) : %x\n", vendorlog->detail_warning_bit.mcu_disable);
+ printf("warning history bit(mcu) : 0x%x%08x\n", le32_to_cpu(vendorlog->detail_warning_his[1]),
+ le32_to_cpu(vendorlog->detail_warning_his[0]));
+ printf("-->high_format_fail : %x\n", vendorlog->detail_warning_his_bit.high_format_fail);
+ printf("-->low_format_fail : %x\n", vendorlog->detail_warning_his_bit.low_format_fail);
+ printf("-->current sensor : %x\n", vendorlog->detail_warning_his_bit.self_test_fail1);
+ printf("-->nand temp sensor : %x\n", vendorlog->detail_warning_his_bit.self_test_fail2);
+ printf("-->board temp sensor : %x\n", vendorlog->detail_warning_his_bit.self_test_fail3);
+ printf("-->cntl temp sensor : %x\n", vendorlog->detail_warning_his_bit.self_test_fail4);
+ printf("-->cap_timer_test_fail : %x\n", vendorlog->detail_warning_his_bit.capacitance_test_fail);
+ printf("-->readOnly_after_rebuild : %x\n", vendorlog->detail_warning_his_bit.readOnly_after_rebuild);
+ printf("-->firmware_loss : %x\n", vendorlog->detail_warning_his_bit.firmware_loss);
+ printf("-->cap_self_test : %x\n", vendorlog->detail_warning_his_bit.cap_unsupply);
+ printf("-->spare_space_warning : %x\n", vendorlog->detail_warning_his_bit.spare_space_warning);
+ printf("-->lifetime_warning : %x\n", vendorlog->detail_warning_his_bit.lifetime_warning);
+ printf("-->temp_high_warning : %x\n", vendorlog->detail_warning_his_bit.temp_high_warning);
+ printf("-->temp_low_warning : %x\n", vendorlog->detail_warning_his_bit.temp_low_warning);
+ printf("-->mcu_disable(mcu) : %x\n", vendorlog->detail_warning_his_bit.mcu_disable);
+
+ for (i = 0; i < 4; i++) {
+ printf("[%d]nand_bytes_written : %" PRIu64 " GB\n", i, le64_to_cpu(vendorlog->nand_bytes_written[i]));
+ }
+
+ for (i = 0; i < 4; i++) {
+ printf("[%d]io_apptag_err : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_apptag_err));
+ printf("[%d]io_guard_err : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_guard_err));
+ printf("[%d]io_reftag_err : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_reftag_err));
+ printf("[%d]io_read_fail_cout : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_read_fail_cout));
+ printf("[%d]io_write_fail_cout : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_write_fail_cout));
+ printf("[%d]io_dma_disable_err : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_dma_disable_err));
+ printf("[%d]io_dma_fatal_err : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_dma_fatal_err));
+ printf("[%d]io_dma_linkdown_err : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_dma_linkdown_err));
+ printf("[%d]io_dma_timeout_err : %u\n", i, le32_to_cpu(vendorlog->io_err[i].io_dma_timeout_err));
+ printf("[%d]lba_err[0] : %u\n", i, le32_to_cpu(vendorlog->io_err[i].lba_err[0]));
+ printf("[%d]lba_err[1] : %u\n", i, le32_to_cpu(vendorlog->io_err[i].lba_err[1]));
+ printf("[%d]lba_err[2] : %u\n", i, le32_to_cpu(vendorlog->io_err[i].lba_err[2]));
+ printf("[%d]lba_err[3] : %u\n", i, le32_to_cpu(vendorlog->io_err[i].lba_err[3]));
+ printf("[%d]lba_err[4] : %u\n", i, le32_to_cpu(vendorlog->io_err[i].lba_err[4]));
+ printf("[%d]lba_err[5] : %u\n", i, le32_to_cpu(vendorlog->io_err[i].lba_err[5]));
+ }
+
+ printf("temp_throttle_per : %u\n", le32_to_cpu(vendorlog->temp_throttle_per));
+ printf("port0_flreset_cnt : %" PRIu64 "\n", le64_to_cpu(vendorlog->port0_fundamental_reset_cnt));
+ printf("port0_hot_reset_cnt : %" PRIu64 "\n", le64_to_cpu(vendorlog->port0_hot_reset_cnt));
+ printf("port0_func_reset_cnt : %" PRIu64 "\n", le64_to_cpu(vendorlog->port0_func_reset_cnt));
+ printf("port0_linkdown_cnt : %" PRIu64 "\n", le64_to_cpu(vendorlog->port0_linkdown_cnt));
+ printf("port0_ctrl_reset_cnt : %" PRIu64 "\n", le64_to_cpu(vendorlog->port0_ctrl_reset_cnt));
+ printf("ces_RcvErr_cnt : %u\n", le32_to_cpu(vendorlog->ces_RcvErr_cnt));
+ printf("ces_BadTlp_cnt : %u\n", le32_to_cpu(vendorlog->ces_BadTlp_cnt));
+ printf("ces_BadDllp_cnt : %u\n", le32_to_cpu(vendorlog->ces_BadDllp_cnt));
+ printf("ces_Rplyover_cnt : %u\n", le32_to_cpu(vendorlog->ces_Rplyover_cnt));
+ printf("ces_RplyTo_cnt : %u\n", le32_to_cpu(vendorlog->ces_RplyTo_cnt));
+ printf("ces_Hlo_cnt : %u\n", le32_to_cpu(vendorlog->ces_Hlo_cnt));
+ printf("scan doorbell err cnt : %u\n", le32_to_cpu(vendorlog->scan_db_err_cnt));
+ printf("doorbell interrupt err cnt : %u\n", le32_to_cpu(vendorlog->db_int_err_cnt));
+
+ printf("------------ncm-----------------------\n");
+ for (i = 0; i < 4; i++) {
+ printf("------------part%d-----------------------\n", i);
+ printf("[%d]nand_rd_unc_count : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_rd_unc_cnt));
+ printf("[%d]nand_rd_srr_count : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_rd_srr_cnt));
+ printf("[%d]nand_rd_sdecode_count : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_rd_soft_decode_cnt));
+ printf("[%d]nand_rd_rb_fail_count : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_rd_rebuild_fail_cnt));
+ printf("[%d]nand_prg_fail_count : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_prg_fail_cnt));
+ printf("[%d]nand_eras_fail_count : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_eras_fail_cnt));
+ printf("[%d]nand_rd_count : %" PRIu64 "\n", i,
+ le64_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_rd_cnt));
+ printf("[%d]nand_prg_count : %" PRIu64 "\n", i,
+ le64_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_prg_cnt));
+ printf("[%d]nand_eras_count : %" PRIu64 "\n", i,
+ le64_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].nand_eras_cnt));
+ printf("[%d]BE_scan_unc_count : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].BE_scan_unc_cnt));
+ printf("[%d]rebuild_req_cnt : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].rebuild_req_cnt));
+ printf("[%d]retry_req_cnt : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].retry_req_cnt));
+ printf("[%d]retry_success_cnt : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].retry_success_cnt));
+ printf("[%d]prg_badblk_num : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].prg_badblk_num));
+ printf("[%d]eras_badblk_num : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].eras_badblk_num));
+ printf("[%d]read_badblk_num : %u\n", i,
+ le32_to_cpu(vendorlog->vendor_log_nandctl_cnt[i].unc_badblk_num));
+ }
+
+ printf("[%d]temp_ctrl_limit_count : %u\n", i, le32_to_cpu(vendorlog->temp_ctrl_limit_cnt));
+ printf("[%d]temp_ctrl_stop_count : %u\n", i, le32_to_cpu(vendorlog->temp_ctrl_stop_cnt));
+ printf("------------wlm-----------------------\n");
+ for (i = 0; i < 4; i++) {
+ printf("------------part%d-----------------------\n", i);
+ printf("[%d]fbb_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].fbb_count));
+ printf("[%d]ebb_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].ebb_count));
+ printf("[%d]lbb_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].lbb_count));
+ printf("[%d]gc_read_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].gc_read_count));
+ printf("[%d]gc_write_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].gc_write_count));
+ printf("[%d]gc_write_fail_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].gc_write_fail_count));
+ printf("[%d]force_gc_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].force_gc_count));
+ printf("[%d]avg_pe_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].avg_pe_count));
+ printf("[%d]max_pe_count : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].max_pe_count));
+ printf("[%d]free_blk_num1 : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].free_blk_num1));
+ printf("[%d]free_blk_num2 : %u\n", i,
+ le32_to_cpu(vendorlog->wearlvl_vendor_log_count[i].free_blk_num2));
+ }
+
+ printf("------------lkm-----------------------\n");
+ printf("[%d]e2e_check_err_count1 : %u\n", i, le32_to_cpu(vendorlog->e2e_check_err_cnt1));
+ printf("[%d]e2e_check_err_count2 : %u\n", i, le32_to_cpu(vendorlog->e2e_check_err_cnt2));
+ printf("[%d]e2e_check_err_count3 : %u\n", i, le32_to_cpu(vendorlog->e2e_check_err_cnt3));
+ printf("[%d]e2e_check_err_count4 : %u\n", i, le32_to_cpu(vendorlog->e2e_check_err_cnt4));
+}
+
+void show_r1_media_err_log(r1_cli_vendor_log_t *vendorlog)
+{
+ int i, j;
+
+ for (i = 0; i < 4; i++) {
+ printf("DM%d read err lba:\n", i);
+ for (j = 0; j < 10; j++) {
+ printf("[%d]lba : %" PRIu64 "\n", j, le64_to_cpu(vendorlog->media_err[i].lba_err[j]));
+ }
+ }
+}
+
+static int nvme_get_vendor_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ __u8 local_mem[BYTE_OF_4K];
+ char *desc = "Get the Inspur vendor log";
+ struct nvme_dev *dev;
+ int err;
+
+ OPT_ARGS(opts) = { OPT_END() };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ memset(local_mem, 0, BYTE_OF_4K);
+ err = nvme_get_log_simple(dev_fd(dev), VENDOR_SMART_LOG_PAGE, sizeof(r1_cli_vendor_log_t), local_mem);
+ if (!err) {
+ show_r1_vendor_log((r1_cli_vendor_log_t *)local_mem);
+ show_r1_media_err_log((r1_cli_vendor_log_t *)local_mem);
+ } else {
+ nvme_show_status(err);
+ }
+
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/inspur/inspur-nvme.h b/plugins/inspur/inspur-nvme.h
new file mode 100644
index 0000000..14a5e76
--- /dev/null
+++ b/plugins/inspur/inspur-nvme.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/inspur/inspur-nvme
+
+#if !defined(INSPUR_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define INSPUR_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("inspur", "Inspur vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("nvme-vendor-log", "Retrieve Inspur Vendor Log, show it", nvme_get_vendor_log)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/inspur/inspur-utils.h b/plugins/inspur/inspur-utils.h
new file mode 100644
index 0000000..d411bf0
--- /dev/null
+++ b/plugins/inspur/inspur-utils.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __INSPUR_UTILS_H__
+#define __INSPUR_UTILS_H__
+
+#define BYTE_OF_64K 65536UL
+#define BYTE_OF_32K 32768UL
+#define BYTE_OF_16K 16384UL
+#define BYTE_OF_4K 4096UL
+#define BYTE_OF_512 512UL
+#define BYTE_OF_256 256UL
+#define BYTE_OF_128 128UL
+
+/* Inspur specific LOG_PAGE_ID */
+typedef enum {
+ VENDOR_SMART_LOG_PAGE = 0xc0,
+} vendor_sepc_log_page_id_e;
+
+#pragma pack(push, 1)
+typedef struct r1_am_cap_transtime {
+ __u32 cap_trans_time1 : 16;
+ __u32 cap_trans_time2 : 16;
+} r1_cap_transtime_t;
+
+typedef struct vendor_warning_bit {
+ __u32 high_format_fail : 1;
+ __u32 low_format_fail : 1;
+ __u32 rebuild_fail1 : 1;
+ __u32 rebuild_fail2 : 1;
+ __u32 rebuild_fail3 : 1;
+ __u32 rebuild_fail4 : 1;
+ __u32 rebuild_fail5 : 1;
+ __u32 rebuild_fail6 : 1;
+ __u32 self_test_fail1 : 1;
+ __u32 self_test_fail2 : 1;
+ __u32 self_test_fail3 : 1;
+ __u32 self_test_fail4 : 1;
+ __u32 internal_err1 : 1;
+ __u32 internal_err2 : 1;
+ __u32 internal_err3 : 1;
+ __u32 internal_err4 : 1;
+ __u32 internal_err5 : 1;
+ __u32 internal_err6 : 1;
+ __u32 internal_err7 : 1;
+ __u32 internal_err8 : 1;
+ __u32 internal_err9 : 1;
+ __u32 internal_err10 : 1;
+ __u32 internal_err11 : 1;
+ __u32 internal_err12 : 1;
+ __u32 internal_err13 : 1;
+ __u32 internal_err14 : 1;
+ __u32 internal_err15 : 1;
+ __u32 internal_err16 : 1;
+ __u32 capacitance_test_fail : 1;
+ __u32 IO_read_fail : 1;
+ __u32 IO_write_fail : 1;
+ __u32 readOnly_after_rebuild : 1;
+ __u32 firmware_loss : 1;
+ __u32 cap_unsupply : 1;
+ __u32 spare_space_warning : 1;
+ __u32 lifetime_warning : 1;
+ __u32 temp_high_warning : 1;
+ __u32 temp_low_warning : 1;
+ __u32 mcu_disable : 1;
+ __u32 rsv : 25;
+} vendor_warning_str;
+
+typedef struct r1_vendor_log_ncm_cout {
+ __u32 nand_rd_unc_cnt;
+ __u32 nand_rd_srr_cnt;
+ __u32 nand_rd_soft_decode_cnt;
+ __u32 nand_rd_rebuild_fail_cnt;
+ __u32 nand_prg_fail_cnt;
+ __u32 nand_eras_fail_cnt;
+ __u64 nand_rd_cnt;
+ __u64 nand_prg_cnt;
+ __u64 nand_eras_cnt;
+ __u32 BE_scan_unc_cnt;
+ __u32 rebuild_req_cnt;
+ __u16 retry_req_cnt;
+ __u16 retry_success_cnt;
+ __u32 prg_badblk_num;
+ __u32 eras_badblk_num;
+ __u32 unc_badblk_num;
+} r1_vendor_log_nandctl_count_t;
+
+typedef struct r1_wearlvl_vendor_log_count {
+ __u32 fbb_count;
+ __u32 ebb_count;
+ __u32 lbb_count;
+ __u32 gc_read_count;
+ __u32 gc_write_count;
+ __u32 gc_write_fail_count;
+ __u32 force_gc_count;
+ __u32 avg_pe_count;
+ __u32 max_pe_count;
+ __u32 free_blk_num1;
+ __u32 free_blk_num2;
+} r1_wearlvl_vendor_log_count_t;
+
+typedef struct vendor_media_err {
+ __u64 lba_err[10];
+} vendor_media_err_t;
+
+typedef struct r1_vendor_log_io_err {
+ __u32 io_guard_err;
+ __u32 io_apptag_err;
+ __u32 io_reftag_err;
+ __u32 io_dma_linkdown_err;
+ __u32 io_dma_disable_err;
+ __u32 io_dma_timeout_err;
+ __u32 io_dma_fatal_err;
+ __u32 io_write_fail_cout;
+ __u32 io_read_fail_cout;
+ __u32 lba_err[6];
+} r1_vendor_log_io_err_t;
+
+typedef struct r1_vendor_log_s {
+ __u32 max_power;
+ __u32 disk_max_temper;
+ __u32 disk_overtemper_cout;
+ __u32 ctrl_max_temper;
+ __u32 ctrl_overtemper_cout;
+ r1_cap_transtime_t cap_transtime;
+ __u32 cap_health_state;
+ __u32 device_state;
+ r1_vendor_log_io_err_t io_err[4];
+ union {
+ vendor_warning_str detail_warning_bit;
+ __u32 detail_warning[2];
+ };
+ union {
+ vendor_warning_str detail_warning_his_bit;
+ __u32 detail_warning_his[2];
+ };
+ __u32 ddr_bit_err_cout;
+ __u32 temp_throttle_per;
+ __u64 port0_fundamental_reset_cnt;
+ __u64 port0_hot_reset_cnt;
+ __u64 port0_func_reset_cnt;
+ __u64 port0_linkdown_cnt;
+ __u64 port0_ctrl_reset_cnt;
+ __u64 nand_bytes_written[4];
+ __u32 power_info;
+ __u32 voltage_info;
+ __u32 current_info;
+ __u32 current_temp[4];
+ __u32 nand_max_temper;
+ __u32 nand_overtemper_cout;
+ __u32 mcu_data_id;
+ __u8 commit_id[16];
+ __u32 ces_RcvErr_cnt;
+ __u32 ces_BadTlp_cnt;
+ __u32 ces_BadDllp_cnt;
+ __u32 ces_Rplyover_cnt;
+ __u32 ces_RplyTo_cnt;
+ __u32 ces_Hlo_cnt;
+ __u32 scan_db_err_cnt;
+ __u32 db_int_err_cnt;
+ __u8 rsvFE[56];
+ r1_vendor_log_nandctl_count_t vendor_log_nandctl_cnt[4];
+ __u32 temp_ctrl_limit_cnt;
+ __u32 temp_ctrl_stop_cnt;
+ __u8 rsvncm[216];
+ r1_wearlvl_vendor_log_count_t wearlvl_vendor_log_count[4];
+ __u8 rsvwlm[512 - sizeof(r1_wearlvl_vendor_log_count_t) * 4 % 512];
+ __u32 e2e_check_err_cnt1;
+ __u32 e2e_check_err_cnt2;
+ __u32 e2e_check_err_cnt3;
+ __u32 e2e_check_err_cnt4;
+ vendor_media_err_t media_err[4];
+ __u8 rsvlkm[176];
+} r1_cli_vendor_log_t;
+#pragma pack(pop)
+
+#endif // __INSPUR_UTILS_H__
diff --git a/plugins/intel/intel-nvme.c b/plugins/intel/intel-nvme.c
new file mode 100644
index 0000000..f660b84
--- /dev/null
+++ b/plugins/intel/intel-nvme.c
@@ -0,0 +1,1740 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "intel-nvme.h"
+
+struct __attribute__((packed)) nvme_additional_smart_log_item {
+ __u8 key;
+ __u8 _kp[2];
+ __u8 norm;
+ __u8 _np;
+ union __attribute__((packed)) {
+ __u8 raw[6];
+ struct __attribute__((packed)) wear_level {
+ __le16 min;
+ __le16 max;
+ __le16 avg;
+ } wear_level;
+ struct __attribute__((packed)) thermal_throttle {
+ __u8 pct;
+ __u32 count;
+ } thermal_throttle;
+ } ;
+ __u8 _rp;
+} ;
+
+struct nvme_additional_smart_log {
+ struct nvme_additional_smart_log_item program_fail_cnt;
+ struct nvme_additional_smart_log_item erase_fail_cnt;
+ struct nvme_additional_smart_log_item wear_leveling_cnt;
+ struct nvme_additional_smart_log_item e2e_err_cnt;
+ struct nvme_additional_smart_log_item crc_err_cnt;
+ struct nvme_additional_smart_log_item timed_workload_media_wear;
+ struct nvme_additional_smart_log_item timed_workload_host_reads;
+ struct nvme_additional_smart_log_item timed_workload_timer;
+ struct nvme_additional_smart_log_item thermal_throttle_status;
+ struct nvme_additional_smart_log_item retry_buffer_overflow_cnt;
+ 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 */
+ __u8 rsvd1[3];
+ __u8 ss;
+ __u8 health[20];
+ __u8 cls;
+ __u8 nlw;
+ __u8 scap;
+ __u8 sstat;
+ __u8 bl[8];
+ __u8 rsvd2[38];
+ __u8 ww[8]; /* little endian */
+ __u8 mic_bl[4];
+ __u8 mic_fw[4];
+};
+
+static void json_intel_id_ctrl(struct nvme_vu_id_ctrl_field *id,
+ char *health, char *bl, char *ww, char *mic_bl, char *mic_fw,
+ struct json_object *root)
+{
+ json_object_add_value_int(root, "ss", id->ss);
+ json_object_add_value_string(root, "health", health );
+ json_object_add_value_int(root, "cls", id->cls);
+ json_object_add_value_int(root, "nlw", id->nlw);
+ json_object_add_value_int(root, "scap", id->scap);
+ json_object_add_value_int(root, "sstat", id->sstat);
+ json_object_add_value_string(root, "bl", bl);
+ json_object_add_value_string(root, "ww", ww);
+ json_object_add_value_string(root, "mic_bl", mic_bl);
+ json_object_add_value_string(root, "mic_fw", mic_fw);
+}
+
+static void intel_id_ctrl(__u8 *vs, struct json_object *root)
+{
+ struct nvme_vu_id_ctrl_field* id = (struct nvme_vu_id_ctrl_field *)vs;
+
+ char health[21] = { 0 };
+ char bl[9] = { 0 };
+ char ww[19] = { 0 };
+ char mic_bl[5] = { 0 };
+ char mic_fw[5] = { 0 };
+
+
+ if (id->health[0]==0)
+ {
+ snprintf(health, 21, "%s", "healthy");
+ }
+ else
+ {
+ snprintf(health, 21, "%s", id->health);
+ }
+
+ snprintf(bl, 9, "%s", id->bl);
+ snprintf(ww, 19, "%02X%02X%02X%02X%02X%02X%02X%02X", id->ww[7],
+ id->ww[6], id->ww[5], id->ww[4], id->ww[3], id->ww[2],
+ id->ww[1], id->ww[0]);
+ snprintf(mic_bl, 5, "%s", id->mic_bl);
+ snprintf(mic_fw, 5, "%s", id->mic_fw);
+
+ if (root) {
+ json_intel_id_ctrl(id, health, bl, ww, mic_bl, mic_fw, root);
+ return;
+ }
+
+ printf("ss : %d\n", id->ss);
+ printf("health : %s\n", health);
+ printf("cls : %d\n", id->cls);
+ printf("nlw : %d\n", id->nlw);
+ printf("scap : %d\n", id->scap);
+ printf("sstat : %d\n", id->sstat);
+ printf("bl : %s\n", bl);
+ printf("ww : %s\n", ww);
+ printf("mic_bl : %s\n", mic_bl);
+ printf("mic_fw : %s\n", mic_fw);
+}
+
+static int id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return __id_ctrl(argc, argv, cmd, plugin, intel_id_ctrl);
+}
+
+static void show_intel_smart_log_jsn(struct nvme_additional_smart_log *smart,
+ unsigned int nsid, const char *devname)
+{
+ struct json_object *root, *entry_stats, *dev_stats, *multi;
+
+ root = json_create_object();
+ json_object_add_value_string(root, "Intel Smart log", devname);
+
+ dev_stats = json_create_object();
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->program_fail_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->program_fail_cnt.raw));
+ json_object_add_value_object(dev_stats, "program_fail_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->erase_fail_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->erase_fail_cnt.raw));
+ json_object_add_value_object(dev_stats, "erase_fail_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->wear_leveling_cnt.norm);
+ multi = json_create_object();
+ json_object_add_value_int(multi, "min", le16_to_cpu(smart->wear_leveling_cnt.wear_level.min));
+ json_object_add_value_int(multi, "max", le16_to_cpu(smart->wear_leveling_cnt.wear_level.max));
+ json_object_add_value_int(multi, "avg", le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg));
+ json_object_add_value_object(entry_stats, "raw", multi);
+ json_object_add_value_object(dev_stats, "wear_leveling", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->e2e_err_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->e2e_err_cnt.raw));
+ json_object_add_value_object(dev_stats, "end_to_end_error_detection_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->crc_err_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->crc_err_cnt.raw));
+ json_object_add_value_object(dev_stats, "crc_error_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_media_wear.norm);
+ json_object_add_value_double(entry_stats, "raw", ((long double)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024);
+ json_object_add_value_object(dev_stats, "timed_workload_media_wear", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_host_reads.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_host_reads.raw));
+ json_object_add_value_object(dev_stats, "timed_workload_host_reads", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_timer.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_timer.raw));
+ json_object_add_value_object(dev_stats, "timed_workload_timer", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->thermal_throttle_status.norm);
+ multi = json_create_object();
+ json_object_add_value_int(multi, "pct", smart->thermal_throttle_status.thermal_throttle.pct);
+ json_object_add_value_int(multi, "cnt", smart->thermal_throttle_status.thermal_throttle.count);
+ json_object_add_value_object(entry_stats, "raw", multi);
+ json_object_add_value_object(dev_stats, "thermal_throttle_status", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->retry_buffer_overflow_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->retry_buffer_overflow_cnt.raw));
+ json_object_add_value_object(dev_stats, "retry_buffer_overflow_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->pll_lock_loss_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->pll_lock_loss_cnt.raw));
+ json_object_add_value_object(dev_stats, "pll_lock_loss_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->nand_bytes_written.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->nand_bytes_written.raw));
+ json_object_add_value_object(dev_stats, "nand_bytes_written", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->host_bytes_written.norm);
+ 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("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)
+{
+ const char *desc = "Get Intel 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";
+ const char *json= "Dump output in json format";
+
+ struct nvme_additional_smart_log smart_log;
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ __u32 namespace_id;
+ bool raw_binary;
+ bool json;
+ };
+
+ 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_FLAG("json", 'j', &cfg.json, json),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xca, sizeof(smart_log),
+ &smart_log);
+ if (!err) {
+ if (cfg.json)
+ show_intel_smart_log_jsn(&smart_log, cfg.namespace_id,
+ dev->name);
+ else if (!cfg.raw_binary)
+ show_intel_smart_log(&smart_log, cfg.namespace_id,
+ dev->name);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ else if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+static int get_market_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Get Intel Marketing Name log and show it.";
+ const char *raw = "dump output in binary format";
+ struct nvme_dev *dev;
+ char log[512];
+ int err;
+
+ struct config {
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xdd, sizeof(log), log);
+ if (!err) {
+ if (!cfg.raw_binary)
+ printf("Intel Marketing Name Log:\n%s\n", log);
+ else
+ d_raw((unsigned char *)&log, sizeof(log));
+ } else if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+struct intel_temp_stats {
+ __le64 curr;
+ __le64 last_overtemp;
+ __le64 life_overtemp;
+ __le64 highest_temp;
+ __le64 lowest_temp;
+ __u8 rsvd[40];
+ __le64 max_operating_temp;
+ __le64 min_operating_temp;
+ __le64 est_offset;
+};
+
+static void show_temp_stats(struct intel_temp_stats *stats)
+{
+ printf(" Intel Temperature Statistics\n");
+ printf("--------------------------------\n");
+ printf("Current temperature : %"PRIu64"\n", le64_to_cpu(stats->curr));
+ printf("Last critical overtemp flag : %"PRIu64"\n", le64_to_cpu(stats->last_overtemp));
+ printf("Life critical overtemp flag : %"PRIu64"\n", le64_to_cpu(stats->life_overtemp));
+ printf("Highest temperature : %"PRIu64"\n", le64_to_cpu(stats->highest_temp));
+ printf("Lowest temperature : %"PRIu64"\n", le64_to_cpu(stats->lowest_temp));
+ printf("Max operating temperature : %"PRIu64"\n", le64_to_cpu(stats->max_operating_temp));
+ printf("Min operating temperature : %"PRIu64"\n", le64_to_cpu(stats->min_operating_temp));
+ printf("Estimated offset : %"PRIu64"\n", le64_to_cpu(stats->est_offset));
+}
+
+static int get_temp_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct intel_temp_stats stats;
+ struct nvme_dev *dev;
+ int err;
+
+ const char *desc = "Get Temperature Statistics log and show it.";
+ const char *raw = "dump output in binary format";
+ struct config {
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xc5, sizeof(stats), &stats);
+ if (!err) {
+ if (!cfg.raw_binary)
+ show_temp_stats(&stats);
+ else
+ d_raw((unsigned char *)&stats, sizeof(stats));
+ } else if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+struct intel_lat_stats {
+ __u16 maj;
+ __u16 min;
+ __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
+#define OPTANE_V1000_BUCKET_LEN 8
+#define NAND_LAT_STATS_LEN 4868
+
+
+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,
+ S
+};
+
+/*
+ * COL_WIDTH controls width of columns in human-readable output.
+ * BUFSIZE is for local temp char[]
+ * US_IN_S and US_IN_MS are for unit conversions when printing.
+ */
+#define COL_WIDTH 12
+#define BUFSIZE 10
+#define US_IN_S 1000000
+#define US_IN_MS 1000
+
+static enum FormatUnit get_seconds_magnitude(__u32 microseconds)
+{
+ if (microseconds > US_IN_S)
+ return S;
+ else if (microseconds > US_IN_MS)
+ return MS;
+ else
+ return US;
+}
+
+static float convert_seconds(__u32 microseconds)
+{
+ float divisor = 1.0;
+
+ if (microseconds > US_IN_S)
+ divisor = US_IN_S;
+ else if (microseconds > US_IN_MS)
+ divisor = US_IN_MS;
+ return microseconds / divisor;
+}
+
+/*
+ * For control over whether a string will format to +/-INF or
+ * print out ####.##US normally.
+ */
+enum inf_bound_type {
+ NEGINF,
+ POSINF,
+ NOINF
+};
+
+/*
+ * Edge buckets may have range [#s, inf) or (-inf, #US] in some
+ * latency statistics formats.
+ * Passing in NEGINF to POSINF to bound_type overrides the string to
+ * either of "-INF" or "+INF", respectively.
+ */
+static void set_unit_string(char *buffer, __u32 microseconds,
+ enum FormatUnit unit,
+ enum inf_bound_type bound_type)
+{
+ char *string;
+
+ if (bound_type != NOINF) {
+ snprintf(buffer, BUFSIZE, "%s", bound_type ? "+INF" : "-INF");
+ return;
+ }
+
+ switch (unit) {
+ case US:
+ string = "us";
+ break;
+ case MS:
+ string = "ms";
+ break;
+ case S:
+ string = "s";
+ break;
+ default:
+ string = "_s";
+ break;
+ }
+
+ snprintf(buffer, BUFSIZE, "%4.2f%s", convert_seconds(microseconds),
+ string);
+}
+
+static void init_buffer(char *buffer, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++)
+ buffer[i] = i + '0';
+}
+
+static void show_lat_stats_bucket(struct intel_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("%-*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)
+{
+ for (int i = (start_offset / bytes_per) - 1;
+ i < end_offset / bytes_per; i++) {
+ if (nonzero_print && stats->data[i] == 0)
+ continue;
+ show_lat_stats_bucket(stats, us_step * i, NOINF,
+ us_step * (i + 1), NOINF, i);
+ }
+}
+
+/*
+ * For 4.0-4.5 revision.
+ */
+#define LATENCY_STATS_V4_BASE_BITS 6
+#define LATENCY_STATS_V4_BASE_VAL (1 << LATENCY_STATS_V4_BASE_BITS)
+
+static int lat_stats_log_scale(int i)
+{
+ // if (i < 128)
+ if (i < (LATENCY_STATS_V4_BASE_VAL << 1))
+ return i;
+
+ int error_bits = (i >> LATENCY_STATS_V4_BASE_BITS) - 1;
+ int base = 1 << (error_bits + LATENCY_STATS_V4_BASE_BITS);
+ int k = i % LATENCY_STATS_V4_BASE_VAL;
+
+ return base + ((k + 0.5) * (1 << error_bits));
+}
+
+/*
+ * Creates a subroot in the following manner:
+ * {
+ * "latstats" : {
+ * "type" : "write" or "read",
+ * "values" : {
+ */
+static void lat_stats_make_json_root(
+ struct json_object *root, struct json_object *bucket_list,
+ int write)
+{
+ struct json_object *subroot = json_create_object();
+
+ json_object_add_value_object(root, "latstats", subroot);
+ json_object_add_value_string(subroot, "type", write ? "write" : "read");
+ json_object_add_value_object(subroot, "values", bucket_list);
+}
+
+/*
+ * Creates a bucket under the "values" json_object. Format is:
+ * "values" : {
+ * "bucket" : {
+ * "id" : #,
+ * "start" : string,
+ * "end" : string,
+ * "value" : 0,
+ * },
+ */
+static void json_add_bucket(struct intel_lat_stats *stats,
+ 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_array_add(bucket_list, 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_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_array_add(bucket_list, 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,
+ __u32 us_step, bool nonzero_print)
+{
+ for (int i = (start_offset / bytes_per) - 1;
+ i < end_offset / bytes_per; i++) {
+ if (nonzero_print && stats->data[i] == 0)
+ continue;
+
+ json_add_bucket(stats, bucket_list,
+ i, us_step * i, NOINF, us_step * (i + 1),
+ NOINF, stats->data[i]);
+ }
+}
+
+static void json_lat_stats_3_0(struct intel_lat_stats *stats,
+ int write)
+{
+ struct json_object *root = json_create_object();
+ struct json_object *bucket_list = json_object_new_array();
+
+ lat_stats_make_json_root(root, bucket_list, write);
+
+ json_lat_stats_linear(stats, bucket_list, 4, 131, 4, 32, false);
+ json_lat_stats_linear(stats, bucket_list, 132, 255, 4, 1024, false);
+ json_lat_stats_linear(stats, bucket_list, 256, 379, 4, 32768, false);
+ json_lat_stats_linear(stats, bucket_list, 380, 383, 4, 32, true);
+ json_lat_stats_linear(stats, bucket_list, 384, 387, 4, 32, true);
+ json_lat_stats_linear(stats, bucket_list, 388, 391, 4, 32, true);
+
+ json_print_object(root, NULL);
+ json_free_object(root);
+}
+
+static void json_lat_stats_4_0(struct intel_lat_stats *stats,
+ int write)
+{
+ struct json_object *root = json_create_object();
+ struct json_object *bucket_list = json_object_new_array();
+
+ lat_stats_make_json_root(root, bucket_list, write);
+
+ __u32 lower_us = 0;
+ __u32 upper_us = 1;
+ bool end = false;
+ int max = 1216;
+
+ for (int i = 0; i < max; i++) {
+ lower_us = lat_stats_log_scale(i);
+ if (i >= max - 1)
+ end = true;
+ else
+ upper_us = lat_stats_log_scale(i + 1);
+
+ json_add_bucket(stats, bucket_list, i,
+ lower_us, NOINF, upper_us,
+ end ? POSINF : NOINF, stats->data[i]);
+ }
+ json_print_object(root, NULL);
+ json_free_object(root);
+}
+
+static void show_lat_stats_3_0(struct intel_lat_stats *stats)
+{
+ show_lat_stats_linear(stats, 4, 131, 4, 32, false);
+ show_lat_stats_linear(stats, 132, 255, 4, 1024, false);
+ show_lat_stats_linear(stats, 256, 379, 4, 32768, false);
+ show_lat_stats_linear(stats, 380, 383, 4, 32, true);
+ show_lat_stats_linear(stats, 384, 387, 4, 32, true);
+ show_lat_stats_linear(stats, 388, 391, 4, 32, true);
+}
+
+static void show_lat_stats_4_0(struct intel_lat_stats *stats)
+{
+ int lower_us = 0;
+ int upper_us = 1;
+ bool end = false;
+ int max = 1216;
+
+ for (int i = 0; i < max; i++) {
+ lower_us = lat_stats_log_scale(i);
+ if (i >= max - 1)
+ end = true;
+ else
+ upper_us = lat_stats_log_scale(i + 1);
+
+ show_lat_stats_bucket(stats, lower_us, NOINF,
+ upper_us, end ? POSINF : NOINF, i);
+ }
+}
+
+static void json_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_object_new_array();
+
+ 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 (media_version[MEDIA_MAJOR_IDX]) {
+ case 3:
+ json_lat_stats_3_0(&stats, write);
+ break;
+ case 4:
+ 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);
+ break;
+ default:
+ printf("Unsupported minor revision (%u.%u)\n",
+ stats.maj, stats.min);
+ break;
+ }
+ break;
+ case 1000:
+ switch (media_version[MEDIA_MINOR_IDX]) {
+ case 0:
+ json_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);
+ break;
+ }
+ printf("\n");
+}
+
+static void print_dash_separator(int count)
+{
+ for (int i = 0; i < count; i++)
+ putchar('-');
+ putchar('\n');
+}
+
+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",
+ 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 (media_version[MEDIA_MAJOR_IDX]) {
+ case 3:
+ show_lat_stats_3_0(&stats);
+ break;
+ case 4:
+ switch (media_version[MEDIA_MINOR_IDX]) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ show_lat_stats_4_0(&stats);
+ break;
+ default:
+ 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);
+ break;
+ }
+}
+
+static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ __u8 data[NAND_LAT_STATS_LEN];
+ struct nvme_dev *dev;
+ int err;
+
+ const char *desc = "Get Intel Latency Statistics log and show it.";
+ 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 {
+ bool raw_binary;
+ bool json;
+ bool write;
+ };
+
+ struct config cfg = {
+ };
+
+ OPT_ARGS(opts) = {
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ /* For optate, latency stats are deleted every time their LID is pulled.
+ * Therefore, we query the longest lat_stats log page first.
+ */
+ err = nvme_get_log_simple(dev_fd(dev), cfg.write ? 0xc2 : 0xc1,
+ sizeof(data), &data);
+
+ media_version[0] = (data[1] << 8) | data[0];
+ media_version[1] = (data[3] << 8) | data[2];
+
+ if (err)
+ goto close_fd;
+
+ if (media_version[0] == 1000) {
+ __u32 thresholds[OPTANE_V1000_BUCKET_LEN] = {0};
+ __u32 result;
+
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = 0xf7,
+ .nsid = 0,
+ .sel = 0,
+ .cdw11 = cfg.write ? 0x1 : 0x0,
+ .uuidx = 0,
+ .data_len = sizeof(thresholds),
+ .data = thresholds,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_get_features(&args);
+ if (err) {
+ fprintf(stderr, "Quering thresholds failed. ");
+ nvme_show_status(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];
+
+ }
+
+ /* Move counter values to optate stats struct which has a
+ * smaller size
+ */
+ memcpy(&v1000_stats, (struct optane_lat_stats *)data,
+ sizeof(struct optane_lat_stats));
+ } else {
+ memcpy(&stats, (struct intel_lat_stats *)data,
+ sizeof(struct intel_lat_stats));
+ }
+
+ if (cfg.json)
+ json_lat_stats(cfg.write);
+ else if (!cfg.raw_binary)
+ show_lat_stats(cfg.write);
+ else {
+ if (media_version[0] == 1000)
+ d_raw((unsigned char *)&v1000_stats,
+ sizeof(v1000_stats));
+ else
+ d_raw((unsigned char *)&stats,
+ sizeof(stats));
+ }
+
+close_fd:
+ dev_close(dev);
+ return err;
+}
+
+struct intel_assert_dump {
+ __u32 coreoffset;
+ __u32 assertsize;
+ __u8 assertdumptype;
+ __u8 assertvalid;
+ __u8 reserved[2];
+};
+
+struct intel_event_dump {
+ __u32 numeventdumps;
+ __u32 coresize;
+ __u32 coreoffset;
+ __u32 eventidoffset[16];
+ __u8 eventIdValidity[16];
+};
+
+struct intel_vu_version {
+ __u16 major;
+ __u16 minor;
+};
+
+struct intel_event_header {
+ __u32 eventidsize;
+ struct intel_event_dump edumps[0];
+};
+
+struct intel_vu_log {
+ struct intel_vu_version ver;
+ __u32 header;
+ __u32 size;
+ __u32 numcores;
+ __u8 reserved[4080];
+};
+
+struct intel_vu_nlog {
+ struct intel_vu_version ver;
+ __u32 logselect;
+ __u32 totalnlogs;
+ __u32 nlognum;
+ __u32 nlogname;
+ __u32 nlogbytesize;
+ __u32 nlogprimarybuffsize;
+ __u32 tickspersecond;
+ __u32 corecount;
+ __u32 nlogpausestatus;
+ __u32 selectoffsetref;
+ __u32 selectnlogpause;
+ __u32 selectaddedoffset;
+ __u32 nlogbufnum;
+ __u32 nlogbufnummax;
+ __u32 coreselected;
+ __u32 reserved[3];
+};
+
+struct intel_cd_log {
+ union {
+ struct {
+ __u32 selectLog : 3;
+ __u32 selectCore : 2;
+ __u32 selectNlog : 8;
+ __u8 selectOffsetRef : 1;
+ __u32 selectNlogPause : 2;
+ __u32 reserved2 : 16;
+ } fields;
+ __u32 entireDword;
+ } u;
+};
+
+static void print_intel_nlog(struct intel_vu_nlog *intel_nlog)
+{
+ printf("Version Major %u\n"
+ "Version Minor %u\n"
+ "Log_select %u\n"
+ "totalnlogs %u\n"
+ "nlognum %u\n"
+ "nlogname %u\n"
+ "nlogbytesze %u\n"
+ "nlogprimarybuffsize %u\n"
+ "tickspersecond %u\n"
+ "corecount %u\n"
+ "nlogpausestatus %u\n"
+ "selectoffsetref %u\n"
+ "selectnlogpause %u\n"
+ "selectaddedoffset %u\n"
+ "nlogbufnum %u\n"
+ "nlogbufnummax %u\n"
+ "coreselected %u\n",
+ intel_nlog->ver.major, intel_nlog->ver.minor,
+ intel_nlog->logselect, intel_nlog->totalnlogs, intel_nlog->nlognum,
+ intel_nlog->nlogname, intel_nlog->nlogbytesize,
+ intel_nlog->nlogprimarybuffsize, intel_nlog->tickspersecond,
+ intel_nlog->corecount, intel_nlog->nlogpausestatus,
+ intel_nlog->selectoffsetref, intel_nlog->selectnlogpause,
+ intel_nlog->selectaddedoffset, intel_nlog->nlogbufnum,
+ intel_nlog->nlogbufnummax, intel_nlog->coreselected);
+}
+
+static int read_entire_cmd(struct nvme_passthru_cmd *cmd, int total_size,
+ const size_t max_tfer, int out_fd, int ioctl_fd,
+ __u8 *buf)
+{
+ int err = 0;
+ size_t dword_tfer = 0;
+
+ dword_tfer = min(max_tfer, total_size);
+ while (total_size > 0) {
+ err = nvme_submit_admin_passthru(ioctl_fd, cmd, NULL);
+ if (err) {
+ fprintf(stderr,
+ "failed on cmd.data_len %u cmd.cdw13 %u cmd.cdw12 %x cmd.cdw10 %u err %x remaining size %d\n",
+ cmd->data_len, cmd->cdw13, cmd->cdw12,
+ cmd->cdw10, err, total_size);
+ goto out;
+ }
+
+ if (out_fd > 0) {
+ err = write(out_fd, buf, cmd->data_len);
+ if (err < 0) {
+ perror("write failure");
+ goto out;
+ }
+ err = 0;
+ }
+ total_size -= dword_tfer;
+ cmd->cdw13 += dword_tfer;
+ cmd->cdw10 = dword_tfer = min(max_tfer, total_size);
+ cmd->data_len = (min(max_tfer, total_size)) * 4;
+ }
+
+ out:
+ return err;
+}
+
+static int write_header(__u8 *buf, int fd, size_t amnt)
+{
+ if (write(fd, buf, amnt) < 0)
+ return 1;
+ return 0;
+}
+
+static int read_header(struct nvme_passthru_cmd *cmd,__u8 *buf, int ioctl_fd,
+ __u32 dw12, int nsid)
+{
+ memset(cmd, 0, sizeof(*cmd));
+ memset(buf, 0, 4096);
+ cmd->opcode = 0xd2;
+ cmd->nsid = nsid;
+ cmd->cdw10 = 0x400;
+ cmd->cdw12 = dw12;
+ cmd->data_len = 0x1000;
+ cmd->addr = (unsigned long)(void *)buf;
+ return read_entire_cmd(cmd, 0x400, 0x400, -1, ioctl_fd, buf);
+}
+
+static int setup_file(char *f, char *file, int fd, int type)
+{
+ struct nvme_id_ctrl ctrl;
+ int err = 0, i = sizeof(ctrl.sn) - 1;
+
+ err = nvme_identify_ctrl(fd, &ctrl);
+ if (err)
+ return err;
+
+ /* Remove trailing spaces from the name */
+ while (i && ctrl.sn[i] == ' ') {
+ ctrl.sn[i] = '\0';
+ i--;
+ }
+
+ sprintf(f, "%s_%-.*s.bin", type == 0 ? "Nlog" :
+ type == 1 ? "EventLog" : "AssertLog",
+ (int)sizeof(ctrl.sn), ctrl.sn);
+ return err;
+}
+
+static int get_internal_log_old(__u8 *buf, int output, int fd,
+ struct nvme_passthru_cmd *cmd)
+{
+ struct intel_vu_log *intel;
+ int err = 0;
+ const int dwmax = 0x400;
+ const int dmamax = 0x1000;
+
+ intel = (struct intel_vu_log *)buf;
+
+ printf("Log major:%d minor:%d header:%d size:%d\n",
+ intel->ver.major, intel->ver.minor, intel->header, intel->size);
+
+ err = write(output, buf, 0x1000);
+ if (err < 0) {
+ perror("write failure");
+ goto out;
+ }
+ intel->size -= 0x400;
+ cmd->opcode = 0xd2;
+ cmd->cdw10 = min(dwmax, intel->size);
+ cmd->data_len = min(dmamax, intel->size);
+ err = read_entire_cmd(cmd, intel->size, dwmax, output, fd, buf);
+ if (err)
+ goto out;
+
+ err = 0;
+ out:
+ return err;
+}
+
+static int get_internal_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ __u8 buf[0x2000];
+ char f[0x100];
+ int err, output, i, j, count = 0, core_num = 1;
+ struct nvme_passthru_cmd cmd;
+ struct intel_cd_log cdlog;
+ struct intel_vu_log *intel = malloc(sizeof(struct intel_vu_log));
+ struct intel_vu_nlog *intel_nlog = (struct intel_vu_nlog *)buf;
+ struct intel_assert_dump *ad = (struct intel_assert_dump *) intel->reserved;
+ struct intel_event_header *ehdr = (struct intel_event_header *)intel->reserved;
+ struct nvme_dev *dev;
+
+ const char *desc = "Get Intel Firmware Log and save it.";
+ const char *log = "Log type: 0, 1, or 2 for nlog, event log, and assert log, respectively.";
+ const char *core = "Select which region log should come from. -1 for all";
+ const char *nlognum = "Select which nlog to read. -1 for all nlogs";
+ const char *file = "Output file; defaults to device name provided";
+ const char *verbose = "To print out verbose nlog info";
+ const char *namespace_id = "Namespace to get logs from";
+
+ struct config {
+ __u32 namespace_id;
+ __u32 log;
+ int core;
+ int lnum;
+ char *file;
+ bool verbose;
+ };
+
+ struct config cfg = {
+ .namespace_id = -1,
+ .file = NULL,
+ .lnum = -1,
+ .core = -1
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("log", 'l', &cfg.log, log),
+ OPT_INT("region", 'r', &cfg.core, core),
+ OPT_INT("nlognum", 'm', &cfg.lnum, nlognum),
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_FILE("output-file", 'o', &cfg.file, file),
+ OPT_FLAG("verbose-nlog", 'v', &cfg.verbose, verbose),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ free(intel);
+ return err;
+ }
+
+ if (cfg.log > 2 || cfg.core > 4 || cfg.lnum > 255) {
+ err = -EINVAL;
+ goto out_free;
+ }
+
+ if (!cfg.file) {
+ err = setup_file(f, cfg.file, dev_fd(dev), cfg.log);
+ if (err)
+ goto out_free;
+ cfg.file = f;
+ }
+
+ cdlog.u.entireDword = 0;
+
+ cdlog.u.fields.selectLog = cfg.log;
+ cdlog.u.fields.selectCore = cfg.core < 0 ? 0 : cfg.core;
+ cdlog.u.fields.selectNlog = cfg.lnum < 0 ? 0 : cfg.lnum;
+
+ output = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (output < 0) {
+ err = output;
+ goto out_free;
+ }
+
+ err = read_header(&cmd, buf, dev_fd(dev), cdlog.u.entireDword,
+ cfg.namespace_id);
+ if (err)
+ goto out;
+ memcpy(intel, buf, sizeof(*intel));
+
+ /* for 1.1 Fultondales will use old nlog, but current assert/event */
+ if ((intel->ver.major < 1 && intel->ver.minor < 1) ||
+ (intel->ver.major <= 1 && intel->ver.minor <= 1 && cfg.log == 0)) {
+ cmd.addr = (unsigned long)(void *)buf;
+ err = get_internal_log_old(buf, output, dev_fd(dev), &cmd);
+ goto out;
+ }
+
+ if (cfg.log == 2) {
+ if (cfg.verbose)
+ printf("Log major:%d minor:%d header:%d size:%d numcores:%d\n",
+ intel->ver.major, intel->ver.minor,
+ intel->header, intel->size, intel->numcores);
+
+ err = write_header(buf, output, 0x1000);
+ if (err) {
+ perror("write failure");
+ goto out;
+ }
+
+ count = intel->numcores;
+ } else if (cfg.log == 0) {
+ if (cfg.lnum < 0)
+ count = intel_nlog->totalnlogs;
+ else
+ count = 1;
+ if (cfg.core < 0)
+ core_num = intel_nlog->corecount;
+ } else if (cfg.log == 1) {
+ core_num = intel->numcores;
+ count = 1;
+ err = write_header(buf, output, sizeof(*intel));
+ if (err)
+ goto out;
+ }
+
+ for (j = (cfg.core < 0 ? 0 : cfg.core);
+ j < (cfg.core < 0 ? core_num : cfg.core + 1);
+ j++) {
+ cdlog.u.fields.selectCore = j;
+ for (i = 0; i < count; i++) {
+ if (cfg.log == 2) {
+ if (!ad[i].assertvalid)
+ continue;
+ cmd.cdw13 = ad[i].coreoffset;
+ cmd.cdw10 = 0x400;
+ cmd.data_len = min(0x400, ad[i].assertsize) * 4;
+ err = read_entire_cmd(&cmd, ad[i].assertsize,
+ 0x400, output,
+ dev_fd(dev),
+ buf);
+ if (err)
+ goto out;
+
+ } else if(cfg.log == 0) {
+ /* If the user selected to read the entire nlog */
+ if (count > 1)
+ cdlog.u.fields.selectNlog = i;
+
+ err = read_header(&cmd, buf, dev_fd(dev),
+ cdlog.u.entireDword,
+ cfg.namespace_id);
+ if (err)
+ goto out;
+ err = write_header(buf, output, sizeof(*intel_nlog));
+ if (err)
+ goto out;
+ if (cfg.verbose)
+ print_intel_nlog(intel_nlog);
+ cmd.cdw13 = 0x400;
+ cmd.cdw10 = 0x400;
+ cmd.data_len = min(0x1000, intel_nlog->nlogbytesize);
+ err = read_entire_cmd(&cmd, intel_nlog->nlogbytesize / 4,
+ 0x400, output,
+ dev_fd(dev),
+ buf);
+ if (err)
+ goto out;
+ } else if (cfg.log == 1) {
+ cmd.cdw13 = ehdr->edumps[j].coreoffset;
+ cmd.cdw10 = 0x400;
+ cmd.data_len = 0x400;
+ err = read_entire_cmd(&cmd, ehdr->edumps[j].coresize,
+ 0x400, output,
+ dev_fd(dev),
+ buf);
+ if (err)
+ goto out;
+ }
+ }
+ }
+ err = 0;
+out:
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ perror("intel log");
+ err = EIO;
+ } else
+ printf("Successfully wrote log to %s\n", cfg.file);
+ close(output);
+out_free:
+ free(intel);
+ dev_close(dev);
+ return err;
+}
+
+static int enable_lat_stats_tracking(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = (
+ "Enable/Disable Intel 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;
+ struct nvme_dev *dev;
+ void *buf = NULL;
+ __u32 result;
+ int err;
+
+ struct config {
+ bool enable, disable;
+ };
+
+ struct config cfg = {
+ .enable = false,
+ .disable = false,
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"enable", 'e', "", CFG_FLAG, &cfg.enable, no_argument, enable_desc},
+ {"disable", 'd', "", CFG_FLAG, &cfg.disable, no_argument, disable_desc},
+ {NULL}
+ };
+
+ err = parse_and_open(&dev, 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 (err)
+ return err;
+
+ struct nvme_get_features_args args_get = {
+ .args_size = sizeof(args_get),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .sel = sel,
+ .cdw11 = cdw11,
+ .uuidx = 0,
+ .data_len = data_len,
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ struct nvme_set_features_args args_set = {
+ .args_size = sizeof(args_set),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .cdw11 = option,
+ .cdw12 = cdw12,
+ .save = save,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = data_len,
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ switch (option) {
+ case None:
+ err = nvme_get_features(&args_get);
+ if (!err) {
+ printf(
+ "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n",
+ fid, result);
+ } else {
+ printf("Could not read feature id 0xE2.\n");
+ dev_close(dev);
+ return err;
+ }
+ break;
+ case True:
+ case False:
+ err = nvme_set_features(&args_set);
+ if (err > 0) {
+ nvme_show_status(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;
+ }
+ dev_close(dev);
+ return err;
+}
+
+static int set_lat_stats_thresholds(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ 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;
+ struct nvme_dev *dev;
+ __u32 result;
+ int err, num;
+
+ struct config {
+ bool write;
+ char *bucket_thresholds;
+ };
+
+ struct config cfg = {
+ .write = false,
+ .bucket_thresholds = "",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("write", 'w', &cfg.write, write),
+ OPT_LIST("bucket-thresholds", 't', &cfg.bucket_thresholds,
+ bucket_thresholds),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+
+ if (err)
+ return err;
+
+ /* 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_simple(dev_fd(dev), 0xc2,
+ sizeof(media_version), media_version);
+ if (err) {
+ fprintf(stderr, "Querying media version failed. ");
+ nvme_show_status(err);
+ goto close_dev;
+ }
+
+ 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_dev;
+
+ }
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .cdw11 = cfg.write ? 0x1 : 0x0,
+ .cdw12 = cdw12,
+ .save = save,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = sizeof(thresholds),
+ .data = thresholds,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ perror("Enable latency tracking");
+ fprintf(stderr, "Command failed while parsing.\n");
+ }
+ } else {
+ fprintf(stderr, "Unsupported command\n");
+ }
+
+close_dev:
+ dev_close(dev);
+ return err;
+}
+
diff --git a/plugins/intel/intel-nvme.h b/plugins/intel/intel-nvme.h
new file mode 100644
index 0000000..165048a
--- /dev/null
+++ b/plugins/intel/intel-nvme.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/intel/intel-nvme
+
+#if !defined(INTEL_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define INTEL_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("intel", "Intel vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ 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)
+ ENTRY("temp-stats", "Retrieve Intel Temperature Statistics log, show it", get_temp_stats_log)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/memblaze/memblaze-nvme.c b/plugins/memblaze/memblaze-nvme.c
new file mode 100644
index 0000000..fb46841
--- /dev/null
+++ b/plugins/memblaze/memblaze-nvme.c
@@ -0,0 +1,1227 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "nvme.h"
+#include "common.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "memblaze-nvme.h"
+#include "memblaze-utils.h"
+
+enum {
+ // 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
+ * Return 1 if @fw1 > @fw2
+ */
+static int compare_fw_version(const char *fw1, const char *fw2)
+{
+ while (*fw1 != '\0') {
+ if (*fw2 == '\0' || *fw1 > *fw2)
+ return 1;
+ if (*fw1 < *fw2)
+ return -1;
+ fw1++;
+ fw2++;
+ }
+
+ if (*fw2 != '\0')
+ return -1;
+
+ return 0;
+}
+
+/**********************************************************
+ * input: firmware version string
+ * output:
+ * 1: new intel format
+ * 0: old memblaze format
+ * *******************************************************/
+#define MEMBLAZE_FORMAT (0)
+#define INTEL_FORMAT (1)
+
+// 2.13 = papaya
+#define IS_PAPAYA(str) (!strcmp(str, "2.13"))
+// 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 *model_name)
+{
+ int logpage_format_type = INTEL_FORMAT;
+ const char *boundary_model_name1 = "P"; // MEMBLAZE P7936DT0640M00
+ const char *boundary_model_name2 = "P5920"; // Use INTEL_FORMAT from Raisin P5920.
+ if (0 == strncmp(model_name, boundary_model_name1, strlen(boundary_model_name1)))
+ {
+ if (strncmp(model_name, boundary_model_name2, strlen(boundary_model_name2)) < 0)
+ {
+ logpage_format_type = MEMBLAZE_FORMAT;
+ }
+ }
+ return logpage_format_type;
+}
+
+static __u32 item_id_2_u32(struct nvme_memblaze_smart_log_item *item)
+{
+ __le32 __id = 0;
+ memcpy(&__id, item->id, 3);
+ return le32_to_cpu(__id);
+}
+
+static __u64 raw_2_u64(const __u8 *buf, size_t len)
+{
+ __le64 val = 0;
+ memcpy(&val, buf, len);
+ return le64_to_cpu(val);
+}
+
+static void get_memblaze_new_smart_info(struct nvme_p4_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 void show_memblaze_smart_log_new(struct nvme_memblaze_smart_log *s,
+ unsigned int nsid, const char *devname)
+{
+ struct nvme_p4_smart_log *smart = (struct nvme_p4_smart_log *)s;
+ __u8 *nm = malloc(NM_SIZE * sizeof(__u8));
+ __u8 *raw = malloc(RAW_SIZE * sizeof(__u8));
+
+ if (!nm) {
+ if (raw)
+ free(raw);
+ return;
+ }
+ if (!raw) {
+ free(nm);
+ return;
+ }
+
+ printf("%s:%s %s:%x\n", "Additional Smart Log for NVME device", devname, "namespace-id", nsid);
+ printf("%-34s%-11s%s\n", "key", "normalized", "raw");
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PROGRAM_FAIL, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "program_fail_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_ERASE_FAIL, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "erase_fail_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_WEARLEVELING_COUNT, nm, raw);
+ printf("%-31s : %3d%% %s%u%s%u%s%u\n", "wear_leveling", *nm,
+ "min: ", *(__u16 *)raw, ", max: ", *(__u16 *)(raw+2), ", avg: ", *(__u16 *)(raw+4));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_E2E_DECTECTION_COUNT, nm, raw);
+ printf("%-31s: %3d%% %"PRIu64"\n", "end_to_end_error_detection_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PCIE_CRC_ERR_COUNT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "crc_error_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR, nm, raw);
+ printf("%-32s: %3d%% %.3f%%\n", "timed_workload_media_wear", *nm, ((float)int48_to_long(raw))/1000);
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"%%\n", "timed_workload_host_reads", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_TIMER, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"%s\n", "timed_workload_timer", *nm, int48_to_long(raw), " min");
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_THERMAL_THROTTLE_STATUS, nm, raw);
+ printf("%-32s: %3d%% %u%%%s%"PRIu64"\n", "thermal_throttle_status", *nm,
+ *raw, ", cnt: ", int48_to_long(raw+1));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "retry_buffer_overflow_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "pll_lock_loss_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_WRITE, nm, raw);
+ printf("%-32s: %3d%% %s%"PRIu64"\n", "nand_bytes_written", *nm, "sectors: ", int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_HOST_WRITE, nm, raw);
+ printf("%-32s: %3d%% %s%"PRIu64"\n", "host_bytes_written", *nm, "sectors: ", int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "system_area_life_left", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_READ, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "total_read", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TEMPT_SINCE_BORN, nm, raw);
+ printf("%-32s: %3d%% %s%u%s%u%s%u\n", "tempt_since_born", *nm,
+ "max: ", *(__u16 *)raw, ", min: ", *(__u16 *)(raw+2), ", curr: ", *(__u16 *)(raw+4));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_POWER_CONSUMPTION, nm, raw);
+ printf("%-32s: %3d%% %s%u%s%u%s%u\n", "power_consumption", *nm,
+ "max: ", *(__u16 *)raw, ", min: ", *(__u16 *)(raw+2), ", curr: ", *(__u16 *)(raw+4));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TEMPT_SINCE_BOOTUP, nm, raw);
+ printf("%-32s: %3d%% %s%u%s%u%s%u\n", "tempt_since_bootup", *nm, "max: ", *(__u16 *)raw,
+ ", min: ", *(__u16 *)(raw+2), ", curr: ", *(__u16 *)(raw+4));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_READ_FAIL, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "read_fail_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_THERMAL_THROTTLE_TIME, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "thermal_throttle_time", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_FLASH_MEDIA_ERROR, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", "flash_media_error", *nm, int48_to_long(raw));
+
+ free(nm);
+ free(raw);
+}
+
+static void show_memblaze_smart_log_old(struct nvme_memblaze_smart_log *smart,
+ unsigned int nsid, const char *devname, const char *fw_ver)
+{
+ char fw_ver_local[STR_VER_SIZE + 1];
+ struct nvme_memblaze_smart_log_item *item;
+
+ strncpy(fw_ver_local, fw_ver, STR_VER_SIZE);
+ *(fw_ver_local + STR_VER_SIZE) = '\0';
+
+ printf("Additional Smart Log for NVME device:%s namespace-id:%x\n", devname, nsid);
+
+ printf("Total write in GB since last factory reset : %"PRIu64"\n",
+ int48_to_long(smart->items[TOTAL_WRITE].rawval));
+ printf("Total read in GB since last factory reset : %"PRIu64"\n",
+ int48_to_long(smart->items[TOTAL_READ].rawval));
+
+ printf("Thermal throttling status[1:HTP in progress] : %u\n",
+ smart->items[THERMAL_THROTTLE].thermal_throttle.on);
+ printf("Total thermal throttling minutes since power on : %u\n",
+ smart->items[THERMAL_THROTTLE].thermal_throttle.count);
+
+ printf("Maximum temperature in Kelvin since last factory reset : %u\n",
+ le16_to_cpu(smart->items[TEMPT_SINCE_RESET].temperature.max));
+ printf("Minimum temperature in Kelvin since last factory reset : %u\n",
+ le16_to_cpu(smart->items[TEMPT_SINCE_RESET].temperature.min));
+ if (compare_fw_version(fw_ver, "0.09.0300") != 0) {
+ printf("Maximum temperature in Kelvin since power on : %u\n",
+ le16_to_cpu(smart->items[TEMPT_SINCE_BOOTUP].temperature_p.max));
+ printf("Minimum temperature in Kelvin since power on : %u\n",
+ le16_to_cpu(smart->items[TEMPT_SINCE_BOOTUP].temperature_p.min));
+ }
+ printf("Current temperature in Kelvin : %u\n",
+ le16_to_cpu(smart->items[TEMPT_SINCE_RESET].temperature.curr));
+
+ printf("Maximum power in watt since power on : %u\n",
+ le16_to_cpu(smart->items[POWER_CONSUMPTION].power.max));
+ printf("Minimum power in watt since power on : %u\n",
+ le16_to_cpu(smart->items[POWER_CONSUMPTION].power.min));
+ printf("Current power in watt : %u\n",
+ le16_to_cpu(smart->items[POWER_CONSUMPTION].power.curr));
+
+ item = &smart->items[POWER_LOSS_PROTECTION];
+ if (item_id_2_u32(item) == 0xEC)
+ printf("Power loss protection normalized value : %u\n",
+ item->power_loss_protection.curr);
+
+ item = &smart->items[WEARLEVELING_COUNT];
+ if (item_id_2_u32(item) == 0xAD) {
+ printf("Percentage of wearleveling count left : %u\n",
+ le16_to_cpu(item->nmval));
+ printf("Wearleveling count min erase cycle : %u\n",
+ le16_to_cpu(item->wearleveling_count.min));
+ printf("Wearleveling count max erase cycle : %u\n",
+ le16_to_cpu(item->wearleveling_count.max));
+ printf("Wearleveling count avg erase cycle : %u\n",
+ le16_to_cpu(item->wearleveling_count.avg));
+ }
+
+ item = &smart->items[HOST_WRITE];
+ if (item_id_2_u32(item) == 0xF5)
+ printf("Total host write in GiB since device born : %llu\n",
+ (unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
+
+ item = &smart->items[THERMAL_THROTTLE_CNT];
+ if (item_id_2_u32(item) == 0xEB)
+ printf("Thermal throttling count since device born : %u\n",
+ item->thermal_throttle_cnt.cnt);
+
+ item = &smart->items[CORRECT_PCIE_PORT0];
+ if (item_id_2_u32(item) == 0xED)
+ printf("PCIE Correctable Error Count of Port0 : %llu\n",
+ (unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
+
+ item = &smart->items[CORRECT_PCIE_PORT1];
+ if (item_id_2_u32(item) == 0xEE)
+ printf("PCIE Correctable Error Count of Port1 : %llu\n",
+ (unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
+
+ item = &smart->items[REBUILD_FAIL];
+ if (item_id_2_u32(item) == 0xEF)
+ printf("End-to-End Error Detection Count : %llu\n",
+ (unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
+
+ item = &smart->items[ERASE_FAIL];
+ if (item_id_2_u32(item) == 0xF0)
+ printf("Erase Fail Count : %llu\n",
+ (unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
+
+ item = &smart->items[PROGRAM_FAIL];
+ if (item_id_2_u32(item) == 0xF1)
+ printf("Program Fail Count : %llu\n",
+ (unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
+
+ item = &smart->items[READ_FAIL];
+ if (item_id_2_u32(item) == 0xF2)
+ printf("Read Fail Count : %llu\n",
+ (unsigned long long)raw_2_u64(item->rawval, sizeof(item->rawval)));
+
+ if ( IS_PAPAYA(fw_ver_local) ) {
+ struct nvme_p4_smart_log *s = (struct nvme_p4_smart_log *)smart;
+ __u8 *nm = malloc(NM_SIZE * sizeof(__u8));
+ __u8 *raw = malloc(RAW_SIZE * sizeof(__u8));
+
+ if (!nm) {
+ if (raw)
+ free(raw);
+ return;
+ }
+ if (!raw) {
+ free(nm);
+ return;
+ }
+ get_memblaze_new_smart_info(s, PROGRAM_FAIL, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ "program_fail_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(s, ERASE_FAIL, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ "erase_fail_count", *nm, int48_to_long(raw));
+
+ get_memblaze_new_smart_info(s, WEARLEVELING_COUNT, nm, raw);
+ printf("%-31s : %3d%% %s%u%s%u%s%u\n",
+ "wear_leveling", *nm, "min: ", *(__u16 *)raw, ", max: ", *(__u16 *)(raw+2), ", avg: ", *(__u16 *)(raw+4));
+
+ get_memblaze_new_smart_info(s, TOTAL_WRITE, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ "nand_bytes_written", *nm, 32*int48_to_long(raw));
+
+ get_memblaze_new_smart_info(s, HOST_WRITE, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ "host_bytes_written", *nm, 32*int48_to_long(raw));
+
+ free(nm);
+ free(raw);
+ }
+}
+
+static int show_memblaze_smart_log(int fd, __u32 nsid, const char *devname,
+ struct nvme_memblaze_smart_log *smart)
+{
+ struct nvme_id_ctrl ctrl;
+ char fw_ver[10];
+ int err = 0;
+
+ 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]);
+
+ if (getlogpage_format_type(ctrl.mn)) // Intel Format & new format
+ {
+ show_memblaze_smart_log_new(smart, nsid, devname);
+ }
+ else // Memblaze Format & old format
+ {
+ show_memblaze_smart_log_old(smart, nsid, devname, fw_ver);
+ }
+ return err;
+}
+
+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");
+ va_end(argp);
+ return 1;
+ }
+
+ if (isalnum((int)*c) == 0) {
+ printf("%s is not a valid number\n", c);
+ va_end(argp);
+ 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;
+ char *desc = "Get Memblaze 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 nvme_dev *dev;
+ struct config {
+ __u32 namespace_id;
+ bool raw_binary;
+ };
+ int err;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_nsid_log(dev_fd(dev), false, 0xca, cfg.namespace_id,
+ sizeof(smart_log), &smart_log);
+ if (!err) {
+ if (!cfg.raw_binary)
+ err = show_memblaze_smart_log(dev_fd(dev),
+ cfg.namespace_id,
+ dev->name, &smart_log);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
+static char *mb_feature_to_string(int feature)
+{
+ 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 mb_get_powermanager_status(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Get Memblaze power management ststus\n (value 0 - 25w, 1 - 20w, 2 - 15w)";
+ __u32 result;
+ __u32 feature_id = MB_FEAT_POWER_MGMT;
+ struct nvme_dev *dev;
+ int err;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = feature_id,
+ .nsid = 0,
+ .sel = 0,
+ .cdw11 = 0,
+ .uuidx = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_get_features(&args);
+ 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)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+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";
+ struct nvme_dev *dev;
+ __u32 result;
+ int err;
+
+ struct config {
+ __u32 feature_id;
+ __u32 value;
+ bool 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = cfg.feature_id,
+ .nsid = 0,
+ .cdw11 = cfg.value,
+ .cdw12 = 0,
+ .save = cfg.save,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ 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)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
+#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 = "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 param1 = 0, param2 = 0;
+ struct nvme_dev *dev;
+ __u32 result;
+ int err;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (parse_params(cfg.param, 2, &param1, &param2)) {
+ printf("setfeature: invalid formats %s\n", cfg.param);
+ dev_close(dev);
+ return EINVAL;
+ }
+ if ((param1 == 1) && (param2 < P2MIN || param2 > P2MAX)) {
+ printf("setfeature: invalid high io latency threshold %d\n", param2);
+ dev_close(dev);
+ return EINVAL;
+ }
+ cfg.value = (param1 << MB_FEAT_HIGH_LATENCY_VALUE_SHIFT) | param2;
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = cfg.feature_id,
+ .nsid = 0,
+ .cdw11 = cfg.value,
+ .cdw12 = 0,
+ .save = false,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ 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)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
+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;
+}
+
+/* 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;
+}
+
+#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 (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);
+ }
+
+ if (fdi) {
+ 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;
+}
+
+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";
+ char buf[LOG_PAGE_SIZE];
+ struct nvme_dev *dev;
+ FILE *fdi = NULL;
+ int err;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ fdi = fopen(FID_C3_LOG_FILENAME, "w+");
+
+ glp_high_latency_show_bar(fdi, DO_PRINT_FLAG);
+ err = nvme_get_log_simple(dev_fd(dev), GLP_ID_VU_GET_HIGH_LATENCY_LOG,
+ sizeof(buf), &buf);
+
+ while (1) {
+ if (!glp_high_latency(fdi, buf, LOG_PAGE_SIZE, DO_PRINT_FLAG)) break;
+ err = nvme_get_log_simple(dev_fd(dev), GLP_ID_VU_GET_HIGH_LATENCY_LOG,
+ sizeof(buf), &buf);
+ if ( err) {
+ nvme_show_status(err);
+ break;
+ }
+ }
+
+ if (NULL != fdi) fclose(fdi);
+ dev_close(dev);
+ return err;
+}
+
+static int memblaze_fw_commit(int fd, int select)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_fw_commit,
+ .cdw10 = 8,
+ .cdw12 = select,
+ };
+
+ return nvme_submit_admin_passthru(fd, &cmd, NULL);
+}
+
+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 "
+ "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=OOB, EEP, ALL)";
+ int xfer = 4096;
+ void *fw_buf;
+ int selectNo,fw_fd,fw_size,err,offset = 0;
+ struct nvme_dev *dev;
+ struct stat sb;
+ int i;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (strlen(cfg.select) != 3) {
+ fprintf(stderr, "Invalid select flag\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ for (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;
+ goto out_close;
+ }
+
+ 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_close;
+ }
+
+ if (posix_memalign(&fw_buf, getpagesize(), fw_size)) {
+ fprintf(stderr, "No memory for f/w size:%d\n", fw_size);
+ err = ENOMEM;
+ goto out_close;
+ }
+
+ if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size))) {
+ err = errno;
+ goto out_free;
+ }
+
+ while (fw_size > 0) {
+ xfer = min(xfer, fw_size);
+
+ struct nvme_fw_download_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .offset = offset,
+ .data_len = xfer,
+ .data = fw_buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ err = nvme_fw_download(&args);
+ if (err < 0) {
+ perror("fw-download");
+ goto out_free;
+ } else if (err != 0) {
+ nvme_show_status(err);
+ goto out_free;
+ }
+ fw_buf += xfer;
+ fw_size -= xfer;
+ offset += xfer;
+ }
+
+ err = memblaze_fw_commit(dev_fd(dev), selectNo);
+
+ if(err == 0x10B || err == 0x20B) {
+ err = 0;
+ fprintf(stderr, "Update successful! Please power cycle for changes to take effect\n");
+ }
+
+out_free:
+ free(fw_buf);
+out_close:
+ close(fw_fd);
+out:
+ dev_close(dev);
+ 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];
+
+ snprintf(subString0, sizeof(subString0), "%d%s", start, unit0);
+ if (end != 0x7FFFFFFF)
+ snprintf(subString1, sizeof(subString1), "%d%s", end, unit1);
+ else
+ 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");
+ }
+
+ if (fdi)
+ 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];
+ char f1[] = FID_C1_LOG_FILENAME;
+ char f2[] = FID_C2_LOG_FILENAME;
+ struct nvme_dev *dev;
+ int err;
+
+ const char *desc = "Get Latency Statistics log and show it.";
+ const char *write = "Get write statistics (read default)";
+
+ struct config {
+ bool write;
+ };
+ struct config cfg = {
+ .write = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("write", 'w', &cfg.write, write),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_log_simple(dev_fd(dev), cfg.write ? 0xc2 : 0xc1,
+ 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
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
+static int memblaze_clear_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ char *desc = "Clear Memblaze devices error log.";
+ struct nvme_dev *dev;
+ int err;
+
+ __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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = cfg.feature_id,
+ .nsid = 0,
+ .cdw11 = cfg.value,
+ .cdw12 = 0,
+ .save = cfg.save,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ 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)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
+static int mb_set_lat_stats(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ 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;
+ struct nvme_dev *dev;
+ void *buf = NULL;
+ __u32 result;
+ int err;
+
+ struct config {
+ bool enable, disable;
+ };
+
+ struct config cfg = {
+ .enable = false,
+ .disable = false,
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"enable", 'e', "", CFG_FLAG, &cfg.enable, no_argument, enable_desc},
+ {"disable", 'd', "", CFG_FLAG, &cfg.disable, no_argument, disable_desc},
+ {NULL}
+ };
+
+ err = parse_and_open(&dev, 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;
+
+ struct nvme_get_features_args args_get = {
+ .args_size = sizeof(args_get),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .sel = sel,
+ .cdw11 = cdw11,
+ .uuidx = 0,
+ .data_len = data_len,
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ struct nvme_set_features_args args_set = {
+ .args_size = sizeof(args_set),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = nsid,
+ .cdw11 = option,
+ .cdw12 = cdw12,
+ .save = save,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = data_len,
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ if (err)
+ return err;
+ switch (option) {
+ case None:
+ err = nvme_get_features(&args_get);
+ if (!err) {
+ printf(
+ "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n",
+ fid, result);
+ } else {
+ printf("Could not read feature id 0xE2.\n");
+ dev_close(dev);
+ return err;
+ }
+ break;
+ case True:
+ case False:
+ err = nvme_set_features(&args_set);
+ if (err > 0) {
+ nvme_show_status(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",
+ 0xe2, option);
+ }
+ break;
+ default:
+ printf("%d not supported.\n", option);
+ err = EINVAL;
+ }
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/memblaze/memblaze-nvme.h b/plugins/memblaze/memblaze-nvme.h
new file mode 100644
index 0000000..e214ca6
--- /dev/null
+++ b/plugins/memblaze/memblaze-nvme.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/memblaze/memblaze-nvme
+
+#if !defined(MEMBLAZE_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define MEMBLAZE_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("memblaze", "Memblaze vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ 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)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/memblaze/memblaze-utils.h b/plugins/memblaze/memblaze-utils.h
new file mode 100644
index 0000000..f64a115
--- /dev/null
+++ b/plugins/memblaze/memblaze-utils.h
@@ -0,0 +1,221 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __MEMBLAZE_UTILS_H__
+#define __MEMBLAZE_UTILS_H__
+
+#define SMART_INFO_OLD_SIZE 512
+#define SMART_INFO_NEW_SIZE 4096
+
+#define ID_SIZE 3
+#define NM_SIZE 2
+#define RAW_SIZE 7
+
+// Intel Format & new format
+/* Raisin Additional smart external ID */
+#define RAISIN_SI_VD_PROGRAM_FAIL_ID 0xAB
+#define RAISIN_SI_VD_ERASE_FAIL_ID 0xAC
+#define RAISIN_SI_VD_WEARLEVELING_COUNT_ID 0xAD
+#define RAISIN_SI_VD_E2E_DECTECTION_COUNT_ID 0xB8
+#define RAISIN_SI_VD_PCIE_CRC_ERR_COUNT_ID 0xC7
+#define RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR_ID 0xE2
+#define RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ_ID 0xE3
+#define RAISIN_SI_VD_TIMED_WORKLOAD_TIMER_ID 0xE4
+#define RAISIN_SI_VD_THERMAL_THROTTLE_STATUS_ID 0xEA
+#define RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT_ID 0xF0
+#define RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT_ID 0xF3
+#define RAISIN_SI_VD_TOTAL_WRITE_ID 0xF4
+#define RAISIN_SI_VD_HOST_WRITE_ID 0xF5
+#define RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT_ID 0xF6
+#define RAISIN_SI_VD_TOTAL_READ_ID 0xFA
+#define RAISIN_SI_VD_TEMPT_SINCE_BORN_ID 0xE7
+#define RAISIN_SI_VD_POWER_CONSUMPTION_ID 0xE8
+#define RAISIN_SI_VD_TEMPT_SINCE_BOOTUP_ID 0xAF
+#define RAISIN_SI_VD_POWER_LOSS_PROTECTION_ID 0xEC
+#define RAISIN_SI_VD_READ_FAIL_ID 0xF2
+#define RAISIN_SI_VD_THERMAL_THROTTLE_TIME_ID 0xEB
+#define RAISIN_SI_VD_FLASH_MEDIA_ERROR_ID 0xED
+
+/* Raisin Addtional smart internal ID */
+typedef enum
+{
+ /* smart attr following intel */
+ RAISIN_SI_VD_PROGRAM_FAIL = 0, /* 0xAB */
+ RAISIN_SI_VD_ERASE_FAIL = 1, /* 0xAC */
+ RAISIN_SI_VD_WEARLEVELING_COUNT = 2,/* 0xAD */
+ RAISIN_SI_VD_E2E_DECTECTION_COUNT = 3, /* 0xB8 */
+ RAISIN_SI_VD_PCIE_CRC_ERR_COUNT = 4, /* 0xC7, 2 port data in one attribute */
+ RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR = 5, /* 0xE2 , unknown definition*/
+ RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ = 6, /* 0xE3 , unknown definition */
+ RAISIN_SI_VD_TIMED_WORKLOAD_TIMER = 7, /* 0xE4 , unknown definition */
+ RAISIN_SI_VD_THERMAL_THROTTLE_STATUS = 8, /* 0xEA */
+ RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT = 9, /* 0xF0, unknown definition*/
+ RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT = 10, /* 0xF3, unknown definition*/
+ RAISIN_SI_VD_TOTAL_WRITE = 11, /* 0xF4, unit is 32MiB */
+ RAISIN_SI_VD_HOST_WRITE = 12, /* 0xF5, unit is 32MiB */
+ RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT = 13, /* 0xF6, unknown definition*/
+ RAISIN_SI_VD_TOTAL_READ = 14, /* 0xFA, unit is 32MiB */
+
+ /* smart attr self defined */
+ RAISIN_SI_VD_TEMPT_SINCE_BORN = 15, /* 0xE7 */
+ RAISIN_SI_VD_POWER_CONSUMPTION = 16, /* 0xE8 */
+ RAISIN_SI_VD_TEMPT_SINCE_BOOTUP = 17, /* 0xAF */
+ RAISIN_SI_VD_POWER_LOSS_PROTECTION = 18, /* 0xEC */
+ RAISIN_SI_VD_READ_FAIL = 19, /* 0xF2 */
+ RAISIN_SI_VD_THERMAL_THROTTLE_TIME = 20, /* 0xEB */
+ RAISIN_SI_VD_FLASH_MEDIA_ERROR = 21, /* 0xED */
+ RAISIN_SI_VD_SMART_INFO_ITEMS_MAX,
+} RAISIN_si_vendor_smart_item_e;
+
+// Memblaze Format & old format
+enum {
+ /*0*/TOTAL_WRITE = 0,
+ /*1*/TOTAL_READ,
+ /*2*/THERMAL_THROTTLE,
+ /*3*/TEMPT_SINCE_RESET,
+ /*4*/POWER_CONSUMPTION,
+ /*5*/TEMPT_SINCE_BOOTUP,
+ /*6*/POWER_LOSS_PROTECTION,
+ /*7*/WEARLEVELING_COUNT,
+ /*8*/HOST_WRITE,
+ /*9*/THERMAL_THROTTLE_CNT,
+ /*10*/CORRECT_PCIE_PORT0,
+ /*11*/CORRECT_PCIE_PORT1,
+ /*12*/REBUILD_FAIL,
+ /*13*/ERASE_FAIL,
+ /*14*/PROGRAM_FAIL,
+ /*15*/READ_FAIL,
+ /*16*/NR_SMART_ITEMS = RAISIN_SI_VD_SMART_INFO_ITEMS_MAX,
+};
+
+// Memblaze Format & old format
+#pragma pack(push, 1)
+struct nvme_memblaze_smart_log_item {
+ __u8 id[3];
+ union {
+ __u8 __nmval[2];
+ __le16 nmval;
+ };
+ union {
+ __u8 rawval[6];
+ struct temperature {
+ __le16 max;
+ __le16 min;
+ __le16 curr;
+ } temperature;
+ struct power {
+ __le16 max;
+ __le16 min;
+ __le16 curr;
+ } power;
+ struct thermal_throttle_mb {
+ __u8 on;
+ __u32 count;
+ } thermal_throttle;
+ struct temperature_p {
+ __le16 max;
+ __le16 min;
+ } temperature_p;
+ struct power_loss_protection {
+ __u8 curr;
+ } power_loss_protection;
+ struct wearleveling_count {
+ __le16 min;
+ __le16 max;
+ __le16 avg;
+ } wearleveling_count;
+ struct thermal_throttle_cnt {
+ __u8 active;
+ __le32 cnt;
+ } thermal_throttle_cnt;
+ };
+ __u8 resv;
+};
+#pragma pack(pop)
+
+struct nvme_memblaze_smart_log {
+ struct nvme_memblaze_smart_log_item items[NR_SMART_ITEMS];
+ __u8 resv[SMART_INFO_OLD_SIZE - sizeof(struct nvme_memblaze_smart_log_item) * NR_SMART_ITEMS];
+};
+
+// Intel Format & new format
+struct nvme_p4_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_p4_smart_log
+{
+ struct nvme_p4_smart_log_item itemArr[NR_SMART_ITEMS];
+
+ /**
+ * change 512 to 4096.
+ * because micron's getlogpage request,the size of many commands have changed to 4k.
+ * request size > user malloc size,casuing parameters that are closed in momery are dirty.
+ */
+ __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 { \
+ if (fdi) \
+ fprintf(fdi, format); \
+ if (print) \
+ printf(format); \
+ } while (0); \
+ }
+
+#define fPRINT_PARAM2(format, value) \
+ { \
+ do { \
+ if (fdi) \
+ fprintf(fdi, format, value); \
+ if (print) \
+ printf(format, value); \
+ } while (0); \
+ }
+
+#endif // __MEMBLAZE_UTILS_H__
diff --git a/plugins/meson.build b/plugins/meson.build
new file mode 100644
index 0000000..c92b208
--- /dev/null
+++ b/plugins/meson.build
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+sources += [
+ 'plugins/amzn/amzn-nvme.c',
+ 'plugins/dell/dell-nvme.c',
+ 'plugins/dera/dera-nvme.c',
+ 'plugins/huawei/huawei-nvme.c',
+ 'plugins/intel/intel-nvme.c',
+ 'plugins/innogrit/innogrit-nvme.c',
+ 'plugins/memblaze/memblaze-nvme.c',
+ 'plugins/micron/micron-nvme.c',
+ 'plugins/netapp/netapp-nvme.c',
+ 'plugins/nvidia/nvidia-nvme.c',
+ 'plugins/scaleflux/sfx-nvme.c',
+ 'plugins/seagate/seagate-nvme.c',
+ 'plugins/shannon/shannon-nvme.c',
+ 'plugins/solidigm/solidigm-nvme.c',
+ 'plugins/toshiba/toshiba-nvme.c',
+ 'plugins/transcend/transcend-nvme.c',
+ 'plugins/virtium/virtium-nvme.c',
+ 'plugins/wdc/wdc-utils.c',
+ 'plugins/wdc/wdc-nvme.c',
+ 'plugins/ymtc/ymtc-nvme.c',
+ 'plugins/zns/zns.c',
+ 'plugins/inspur/inspur-nvme.c',
+ 'plugins/fdp/fdp.c',
+]
+subdir('solidigm')
+subdir('ocp')
diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c
new file mode 100644
index 0000000..3c0904c
--- /dev/null
+++ b/plugins/micron/micron-nvme.c
@@ -0,0 +1,3394 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include <limits.h>
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "micron-nvme.h"
+
+/* Supported Vendor specific feature ids */
+#define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS 0xC3
+#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xC1
+#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 C0_log_size 512
+#define C2_log_size 4096
+#define D0_log_size 512
+#define FB_log_size 512
+#define E1_log_size 256
+#define MaxLogChunk 16 * 1024
+#define CommonChunkSize 16 * 4096
+
+#define min(x, y) ((x) > (y) ? (y) : (x))
+#define SensorCount 8
+
+/* Plugin version major_number.minor_number.patch */
+static const char *__version_major = "1";
+static const char *__version_minor = "0";
+static const char *__version_patch = "14";
+
+/* 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;
+} LogPageHeader_t;
+
+static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg)
+{
+ char tempFolder[8192] = { 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 ReadSysFile(const char *file, unsigned short *id)
+{
+ int ret = 0;
+ char idstr[32] = { '\0' };
+ int fd = open(file, O_RDONLY);
+
+ if (fd < 0) {
+ perror(file);
+ return fd;
+ }
+
+ ret = read(fd, idstr, sizeof(idstr));
+ close(fd);
+ if (ret < 0)
+ perror("read");
+ 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 0x5196:
+ 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;
+ 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)
+{
+ 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) {
+ free(fileLocation);
+ goto exit_status;
+ }
+ strcpy(strTemp, fileLocation);
+ strcat(strTemp, "/");
+ free(fileLocation);
+
+ length = (int)strlen(strTemp);
+ if ((fileLocation = (char *)malloc(length + 1)) == NULL) {
+ free(strTemp);
+ 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++;
+ }
+
+ if (mkdir(strMainDirName, 0777) < 0) {
+ err = -1;
+ goto exit_status;
+ }
+
+ if (strOSDirName != NULL) {
+ sprintf(strOSDirName, "%s/%s", strMainDirName, "OS");
+ if (mkdir(strOSDirName, 0777) < 0) {
+ rmdir(strMainDirName);
+ err = -1;
+ goto exit_status;
+ }
+ }
+ if (strCtrlDirName != NULL) {
+ sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller");
+ if (mkdir(strCtrlDirName, 0777) < 0) {
+ if (strOSDirName != NULL)
+ rmdir(strOSDirName);
+ rmdir(strMainDirName);
+ err = -1;
+ }
+ }
+
+exit_status:
+ return err;
+}
+
+static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize)
+{
+ int err = 0;
+ unsigned char pTmpBuf[CommonChunkSize] = { 0 };
+ LogPageHeader_t *pLogHeader = NULL;
+
+ if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) {
+ err = nvme_get_log_simple(nFD, ucLogID,
+ 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 (ignored)!\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_passthru_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_admin_passthru(nFD, &cmd, NULL);
+ ullBytesRead += uiXferDwords * 4;
+ pTempPtr = pBuffer + ullBytesRead;
+ }
+
+ return err;
+}
+
+static int NVMEResetLog(int nFD, unsigned char ucLogID, int nBufferSize,
+ long long llMaxSize)
+{
+ unsigned int *pBuffer = NULL;
+ int err = 0;
+
+ 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) {
+ free(pBuffer);
+ return err;
+ }
+
+ if (pBuffer[0] == 0xdeadbeef)
+ break;
+
+ llMaxSize = llMaxSize - nBufferSize;
+ }
+
+ free(pBuffer);
+ return err;
+}
+
+static int GetCommonLogPage(int nFD, unsigned char ucLogID,
+ unsigned char **pBuffer, int nBuffSize)
+{
+ 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_simple(nFD, ucLogID, nBuffSize, pTempPtr);
+ *pBuffer = pTempPtr;
+
+exit_status:
+ return err;
+}
+
+/*
+ * Plugin Commands
+ */
+static int micron_parse_options(struct nvme_dev **dev, int argc, char **argv,
+ const char *desc,
+ const struct argconfig_commandline_options *opts,
+ eDriveModel *modelp)
+{
+ int idx = 0;
+ int err = parse_and_open(dev, argc, argv, desc, opts);
+
+ if (err) {
+ perror("open");
+ return -1;
+ }
+
+ if (modelp) {
+ sscanf(argv[optind], "/dev/nvme%d", &idx);
+ *modelp = GetDriveModel(idx);
+ }
+
+ return 0;
+}
+
+static int micron_fw_commit(int fd, int select)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_fw_commit,
+ .cdw10 = 8,
+ .cdw12 = select,
+ };
+ return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+}
+
+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 selectNo, fw_fd, fw_size, err, offset = 0;
+ struct nvme_dev *dev;
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (strlen(cfg.select) != 3) {
+ fprintf(stderr, "Invalid select flag\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ 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");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ fw_fd = open(cfg.fw, O_RDONLY);
+ if (fw_fd < 0) {
+ fprintf(stderr, "no firmware file provided\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ err = fstat(fw_fd, &sb);
+ if (err < 0) {
+ perror("fstat");
+ err = errno;
+ goto out;
+ }
+
+ 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))) {
+ err = errno;
+ goto out_free;
+ }
+
+ while (fw_size > 0) {
+ xfer = min(xfer, fw_size);
+
+ struct nvme_fw_download_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .offset = offset,
+ .data_len = xfer,
+ .data = fw_buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ err = nvme_fw_download(&args);
+ if (err < 0) {
+ perror("fw-download");
+ goto out_free;
+ } else if (err != 0) {
+ nvme_show_status(err);
+ goto out_free;
+ }
+ fw_buf += xfer;
+ fw_size -= xfer;
+ offset += xfer;
+ }
+
+ err = micron_fw_commit(dev_fd(dev), selectNo);
+
+ if (err == 0x10B || err == 0x20B) {
+ err = 0;
+ fprintf(stderr,
+ "Update successful! Power cycle for changes to take effect\n");
+ }
+
+out_free:
+ free(fw_buf);
+out:
+ close(fw_fd);
+ dev_close(dev);
+ return err;
+}
+
+static int micron_smbus_option(int argc, char **argv,
+ struct command *cmd, struct plugin *plugin)
+{
+ __u32 result = 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 fid = MICRON_FEATURE_SMBUS_OPTION;
+ eDriveModel model = UNKNOWN_MODEL;
+ struct nvme_dev *dev;
+ int err = 0;
+
+ 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()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return err;
+
+ if (model != M5407 && model != M5411) {
+ printf ("This option is not supported for specified drive\n");
+ dev_close(dev);
+ return err;
+ }
+
+ if (!strcmp(opt.option, "enable")) {
+ cdw11 = opt.value << 1 | 1;
+ err = nvme_set_features_simple(dev_fd(dev), fid, 1, cdw11, opt.save,
+ &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")) {
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = 1,
+ .sel = opt.value,
+ .cdw11 = 0,
+ .uuidx = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_get_features(&args);
+ 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_features_simple(dev_fd(dev), fid, 1, cdw11, opt.save,
+ &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);
+ dev_close(dev);
+ return -1;
+ }
+
+ close(dev_fd(dev));
+ return err;
+}
+
+static int micron_temp_stats(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+
+ 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;
+ struct nvme_dev *dev;
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("format", 'f', &cfg.fmt, fmt),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+
+ if (strcmp(cfg.fmt, "json") == 0)
+ is_json = true;
+
+ err = nvme_get_log_smart(dev_fd(dev), 0xffffffff, false, &smart_log);
+ if (!err) {
+ temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]);
+ temperature = temperature ? temperature - 273 : 0;
+ for (i = 0; i < SensorCount && tempSensors[i] != 0; 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 && tempSensors[i] != 0; 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 && tempSensors[i] != 0; i++) {
+ printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]);
+ }
+ }
+ }
+ dev_close(dev);
+ return err;
+}
+
+static int micron_pcie_stats(int argc, char **argv,
+ struct command *cmd, struct plugin *plugin)
+{
+ int i, err = 0, bus = 0, domain = 0, device = 0, function = 0, ctrlIdx;
+ char strTempFile[1024], strTempFile2[1024], command[1024];
+ struct nvme_dev *dev;
+ 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 };
+ struct nvme_passthru_cmd admin_cmd = { 0 };
+ eDriveModel eModel = UNKNOWN_MODEL;
+ char *res;
+ bool is_json = true;
+ bool counters = false;
+ struct format {
+ char *fmt;
+ };
+ const char *desc = "Retrieve PCIe event counters";
+ const char *fmt = "output format json|normal";
+ struct format cfg = {
+ .fmt = "json",
+ };
+ struct pcie_error_counters {
+ __u16 receiver_error;
+ __u16 bad_tlp;
+ __u16 bad_dllp;
+ __u16 replay_num_rollover;
+ __u16 replay_timer_timeout;
+ __u16 advisory_non_fatal_error;
+ __u16 DLPES;
+ __u16 poisoned_tlp;
+ __u16 FCPC;
+ __u16 completion_timeout;
+ __u16 completion_abort;
+ __u16 unexpected_completion;
+ __u16 receiver_overflow;
+ __u16 malformed_tlp;
+ __u16 ecrc_error;
+ __u16 unsupported_request_error;
+ } pcie_error_counters = { 0 };
+
+ struct {
+ char *err;
+ int bit;
+ int val;
+ } pcie_correctable_errors[] = {
+ { "Unsupported Request Error Status (URES)", 20,
+ offsetof(struct pcie_error_counters, unsupported_request_error)},
+ { "ECRC Error Status (ECRCES)", 19,
+ offsetof(struct pcie_error_counters, ecrc_error)},
+ { "Malformed TLP Status (MTS)", 18,
+ offsetof(struct pcie_error_counters, malformed_tlp)},
+ { "Receiver Overflow Status (ROS)", 17,
+ offsetof(struct pcie_error_counters, receiver_overflow)},
+ { "Unexpected Completion Status (UCS)", 16,
+ offsetof(struct pcie_error_counters, unexpected_completion)},
+ { "Completer Abort Status (CAS)", 15,
+ offsetof(struct pcie_error_counters, completion_abort)},
+ { "Completion Timeout Status (CTS)", 14,
+ offsetof(struct pcie_error_counters, completion_timeout)},
+ { "Flow Control Protocol Error Status (FCPES)", 13,
+ offsetof(struct pcie_error_counters, FCPC)},
+ { "Poisoned TLP Status (PTS)", 12,
+ offsetof(struct pcie_error_counters, poisoned_tlp)},
+ { "Data Link Protocol Error Status (DLPES)", 4,
+ offsetof(struct pcie_error_counters, DLPES)},
+ },
+ pcie_uncorrectable_errors[] = {
+ { "Advisory Non-Fatal Error Status (ANFES)", 13,
+ offsetof(struct pcie_error_counters, advisory_non_fatal_error)},
+ { "Replay Timer Timeout Status (RTS)", 12,
+ offsetof(struct pcie_error_counters, replay_timer_timeout)},
+ { "REPLAY_NUM Rollover Status (RRS)", 8,
+ offsetof(struct pcie_error_counters, replay_num_rollover)},
+ { "Bad DLLP Status (BDS)", 7,
+ offsetof(struct pcie_error_counters, bad_dllp)},
+ { "Bad TLP Status (BTS)", 6,
+ offsetof(struct pcie_error_counters, bad_tlp)},
+ { "Receiver Error Status (RES)", 0,
+ offsetof(struct pcie_error_counters, receiver_error)},
+ };
+
+ __u32 correctable_errors;
+ __u32 uncorrectable_errors;
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("format", 'f', &cfg.fmt, fmt),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ 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");
+ goto out;
+ }
+
+ if (strcmp(cfg.fmt, "normal") == 0)
+ is_json = false;
+
+ if (eModel == M5407) {
+ admin_cmd.opcode = 0xD6;
+ admin_cmd.addr = (__u64)(uintptr_t)&pcie_error_counters;
+ admin_cmd.data_len = sizeof(pcie_error_counters);
+ admin_cmd.cdw10 = 1;
+ err = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+ if (!err) {
+ counters = true;
+ correctable_errors = 10;
+ uncorrectable_errors = 6;
+ goto print_stats;
+ }
+ }
+
+ 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);
+ memset(strTempFile2, 0x0, 1024);
+ sLinkSize = readlink(strTempFile, strTempFile2, 1023);
+ 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);
+ memset(strTempFile2, 0x0, 1024);
+ sLinkSize = readlink(strTempFile, strTempFile2, 1023);
+ 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");
+ pclose(fp);
+ 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");
+ pclose(fp);
+ goto out;
+ }
+ pclose(fp);
+
+ correctable_errors = (__u32)strtol(correctable, NULL, 16);
+ uncorrectable_errors = (__u32)strtol(uncorrectable, NULL, 16);
+
+print_stats:
+ if (is_json) {
+
+ struct json_object *root = json_create_object();
+ struct json_object *pcieErrors = json_create_array();
+ struct json_object *stats = json_create_object();
+ __u8 *pcounter = (__u8 *)&pcie_error_counters;
+
+ json_object_add_value_array(root, "PCIE Stats", pcieErrors);
+ for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) {
+ __u16 val = counters ? *(__u16 *)(pcounter + pcie_correctable_errors[i].val) :
+ (correctable_errors >> pcie_correctable_errors[i].bit) & 1;
+ json_object_add_value_int(stats, pcie_correctable_errors[i].err, val);
+ }
+ for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) {
+ __u16 val = counters ? *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val) :
+ (uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1;
+ json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, val);
+ }
+ json_array_add_value_object(pcieErrors, stats);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ } else if (counters == true) {
+ __u8 *pcounter = (__u8 *)&pcie_error_counters;
+ for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) {
+ printf("%-42s : %-1hu\n", pcie_correctable_errors[i].err,
+ *(__u16 *)(pcounter + pcie_correctable_errors[i].val));
+ }
+ for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) {
+ printf("%-42s : %-1hu\n", pcie_uncorrectable_errors[i].err,
+ *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val));
+ }
+ } else if (eModel == M5407 || eModel == M5410) {
+ for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) {
+ printf("%-42s : %-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("%-42s : %-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:
+ dev_close(dev);
+ return err;
+}
+
+static int micron_clear_pcie_correctable_errors(int argc, char **argv,
+ struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = -EINVAL, bus = 0, domain = 0, device = 0, function = 0;
+ char strTempFile[1024], strTempFile2[1024], command[1024];
+ struct nvme_dev *dev;
+ char *businfo = NULL;
+ char *devicename = NULL;
+ char tdevice[PATH_MAX] = { 0 };
+ ssize_t sLinkSize = 0;
+ eDriveModel model = UNKNOWN_MODEL;
+ struct nvme_passthru_cmd admin_cmd = { 0 };
+ char correctable[8] = { 0 };
+ 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()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return err;
+
+ /* For M51CX models, PCIe errors are cleared using 0xC3 feature */
+ if (model == M51CX) {
+ err = nvme_set_features_simple(dev_fd(dev), fid, 0, (1 << 31), false,
+ &result);
+ if (err == 0 && (err = (int)result) == 0) {
+ printf("Device correctable errors are cleared!\n");
+ goto out;
+ }
+ } else if (model == M5407) {
+ admin_cmd.opcode = 0xD6;
+ admin_cmd.addr = 0;
+ admin_cmd.cdw10 = 0;
+ err = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+ if (err == 0) {
+ printf("Device correctable error counters are cleared!\n");
+ goto out;
+ } else {
+ /* proceed to clear status bits using sysfs interface
+ printf("Error clearing PCIe correctable errors = 0x%x\n", err); */
+ }
+ }
+
+ 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;
+
+ memset(strTempFile2, 0x0, 1024);
+ sLinkSize = readlink(strTempFile, strTempFile2, 1023);
+ 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;
+ memset(strTempFile2, 0x0, 1024);
+ sLinkSize = readlink(strTempFile, strTempFile2, 1023);
+ 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");
+ pclose(fp);
+ goto out;
+ }
+ pclose(fp);
+ printf("Device correctable errors cleared!\n");
+ printf("Device correctable errors detected: %s\n", correctable);
+ err = 0;
+out:
+ dev_close(dev);
+ return err;
+}
+
+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;
+
+ memcpy(logD0, buf, sizeof(logD0));
+
+
+ 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; /* FB client spec version 1.0 sizes - M5410 models */
+ int size2; /* FB client spec version 0.7 sizes - M5407 models */
+}
+/* Smart Health Log information as per OCP spec M51CX models */
+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},
+},
+/* Extended SMART log information */
+e1_log_page[] = {
+ { "Reserved", 12},
+ { "Grown Bad Block Count", 4},
+ { "Per Block Max Erase Count", 4},
+ { "Power On Minutes", 4},
+ { "Reserved", 24},
+ { "Write Protect Reason", 4},
+ { "Reserved", 12},
+ { "Drive Capacity", 8},
+ { "Reserved", 8},
+ { "Total Erase Count", 8},
+ { "Lifetime Use Rate", 8},
+ { "Erase Fail Count", 8},
+ { "Reserved", 8},
+ { "Reported UC Errors", 8},
+ { "Reserved", 24},
+ { "Program Fail Count", 16},
+ { "Total Bytes Read", 16},
+ { "Total Bytes Written", 16},
+ { "Reserved", 16},
+ { "TU Size", 4},
+ { "Total Block Stripe Count", 4},
+ { "Free Block Stripe Count", 4},
+ { "Block Stripe Size", 8},
+ { "Reserved", 16},
+ { "User Block Min Erase Count", 4},
+ { "User Block Avg Erase Count", 4},
+ { "User Block Max Erase Count", 4},
+},
+/* Vendor Specific Health Log information */
+fb_log_page[] = {
+ { "Physical Media Units Written - TLC", 16, 16 },
+ { "Physical Media Units Written - SLC", 16, 16 },
+ { "Normalized Bad User NAND Block Count", 2, 2},
+ { "Raw Bad User NAND Block Count", 6, 6},
+ { "XOR Recovery Count", 8, 8},
+ { "Uncorrectable Read Error Count", 8, 8},
+ { "SSD End to End Corrected Errors", 8, 8},
+ { "SSD End to End Detected Counts", 4, 8},
+ { "SSD End to End Uncorrected Counts", 4, 8},
+ { "System data % life-used", 1, 1},
+ { "Reserved", 0, 3},
+ { "Minimum User Data Erase Count - TLC", 8, 8},
+ { "Maximum User Data Erase Count - TLC", 8, 8},
+ { "Average User Data Erase Count - TLC", 0, 8},
+ { "Minimum User Data Erase Count - SLC", 8, 8},
+ { "Maximum User Data Erase Count - SLC", 8, 8},
+ { "Average User Data Erase Count - SLC", 0, 8},
+ { "Normalized Program Fail Count", 2, 2},
+ { "Raw Program Fail Count", 6, 6},
+ { "Normalized Erase Fail Count", 2, 2},
+ { "Raw Erase Fail Count", 6, 6},
+ { "Pcie Correctable Error Count", 8, 8},
+ { "% Free Blocks (User)", 1, 1},
+ { "Reserved", 0, 3},
+ { "Security Version Number", 8, 8},
+ { "% Free Blocks (System)", 1, 1},
+ { "Reserved", 0, 3},
+ { "Dataset Management (Deallocate) Commands", 16, 16},
+ { "Incomplete TRIM Data", 8, 8},
+ { "% Age of Completed TRIM", 1, 2},
+ { "Background Back-Pressure Gauge", 1, 1},
+ { "Reserved", 0, 3},
+ { "Soft ECC Error Count", 8, 8},
+ { "Refresh Count", 8, 8},
+ { "Normalized Bad System NAND Block Count", 2, 2},
+ { "Raw Bad System NAND Block Count", 6, 6},
+ { "Endurance Estimate", 16, 16},
+ { "Thermal Throttling Status", 1, 1},
+ { "Thermal Throttling Count", 1, 1},
+ { "Unaligned I/O", 8, 8},
+ { "Physical Media Units Read", 16, 16},
+ { "Reserved", 279, 0},
+ { "Log Page Version", 2, 0},
+ { "READ CMDs exceeding threshold", 0, 4},
+ { "WRITE CMDs exceeding threshold", 0, 4},
+ { "TRIMs CMDs exceeding threshold", 0, 4},
+ { "Reserved", 0, 4},
+ { "Reserved", 0, 210},
+ { "Log Page Version", 0, 2},
+ { "Log Page GUID", 0, 16},
+};
+
+/* 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 */
+ __u8 spec /* ocp spec index */
+)
+{
+ __u64 lval_lo, lval_hi;
+ __u32 ival;
+ __u16 sval;
+ __u8 cval, lval[8] = { 0 };
+ int field;
+ int offset = 0;
+
+ for (field = 0; field < field_count; field++) {
+ char datastr[1024] = { 0 };
+ char *sfield = NULL;
+ int size = (spec == 0) ? log_page[field].size : log_page[field].size2;
+ if (size == 0) continue;
+ sfield = log_page[field].field;
+ if (size == 16) {
+ if (strstr(sfield, "GUID")) {
+ sprintf(datastr, "0x%"PRIx64"%"PRIx64"",
+ (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset + 8])),
+ (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset])));
+ } else {
+ lval_lo = *((__u64 *)(&buf[offset]));
+ lval_hi = *((__u64 *)(&buf[offset + 8]));
+ if (lval_hi)
+ sprintf(datastr, "0x%"PRIx64"%016"PRIx64"",
+ le64_to_cpu(lval_hi), le64_to_cpu(lval_lo));
+ else
+ sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo));
+ }
+ } else if (size == 8) {
+ lval_lo = *((__u64 *)(&buf[offset]));
+ sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo));
+ } else if (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 (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 (size == 4) {
+ ival = *((__u32 *)(&buf[offset]));
+ sprintf(datastr, "0x%x", le32_to_cpu(ival));
+ } else if (size == 2) {
+ sval = *((__u16 *)(&buf[offset]));
+ sprintf(datastr, "0x%04x", le16_to_cpu(sval));
+ } else if (size == 1) {
+ cval = buf[offset];
+ sprintf(datastr, "0x%02x", cval);
+ } else {
+ sprintf(datastr, "0");
+ }
+ offset += size;
+ /* do not print reserved values */
+ if (strstr(sfield, "Reserved"))
+ continue;
+ if (stats != NULL) {
+ json_object_add_value_string(stats, sfield, datastr);
+ } else {
+ printf("%-40s : %-4s\n", sfield, datastr);
+ }
+ }
+}
+
+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, 0);
+
+ if (is_json) {
+ json_array_add_value_object(logPages, stats);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ }
+}
+
+static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json, __u8 spec)
+{
+ 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, spec);
+
+ /* print last three entries from D0 log page */
+ if (buf2 != NULL) {
+ init_d0_log_page(buf2, nsze);
+
+ if (is_json) {
+ for (int i = 0; i < 7; i++) {
+ json_object_add_value_string(stats,
+ d0_log_page[i].field,
+ d0_log_page[i].datastr);
+ }
+ } else {
+ for (int i = 0; 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);
+ }
+}
+
+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 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)
+{
+ 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;
+ struct nvme_dev *dev;
+ int 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+
+ if (strcmp(cfg.fmt, "normal") == 0)
+ is_json = false;
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (err) {
+ printf("Error %d retrieving controller identification data\n", err);
+ goto out;
+ }
+
+ /* pull log details based on the model name */
+ sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx);
+ eModel = GetDriveModel(ctrlIdx);
+ if ((eModel == UNKNOWN_MODEL) || (eModel == M51CX)) {
+ printf ("Unsupported drive model for vs-nand-stats command\n");
+ err = -1;
+ goto out;
+ }
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xD0, 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_simple(dev_fd(dev), 0xFB, 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) {
+ __u8 spec = (eModel == M5410) ? 0 : 1; /* FB spec version */
+ print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json, spec);
+ } 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:
+ dev_close(dev);
+ if (err > 0)
+ nvme_show_status(err);
+
+ return err;
+}
+
+static void print_ext_smart_logs_e1(__u8 *buf, bool is_json)
+{
+ struct json_object *root;
+ struct json_object *logPages;
+ struct json_object *stats = NULL;
+ int field_count = sizeof(e1_log_page)/sizeof(e1_log_page[0]);
+
+ if (is_json) {
+ root = json_create_object();
+ stats = json_create_object();
+ logPages = json_create_array();
+ json_object_add_value_array(root, "SMART Extended Log:0xE1", logPages);
+ }
+ else {
+ printf("SMART Extended Log:0xE1\n");
+ }
+
+ print_micron_vs_logs(buf, e1_log_page, field_count, stats, 0);
+
+ if (is_json) {
+ json_array_add_value_object(logPages, stats);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ }
+}
+
+static int micron_smart_ext_log(int argc, char **argv,
+ struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Retrieve extended SMART logs for the given device ";
+ unsigned int extSmartLog[E1_log_size/sizeof(int)] = { 0 };
+ eDriveModel eModel = UNKNOWN_MODEL;
+ int err = 0, ctrlIdx = 0;
+ struct nvme_dev *dev;
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+ if (strcmp(cfg.fmt, "normal") == 0)
+ is_json = false;
+
+ sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx);
+ if ((eModel = GetDriveModel(ctrlIdx)) != M51CX) {
+ printf ("Unsupported drive model for vs-smart-ext-log command\n");
+ err = -1;
+ goto out;
+ }
+ err = nvme_get_log_simple(dev_fd(dev), 0xE1, E1_log_size, extSmartLog);
+ if (!err) {
+ print_ext_smart_logs_e1((__u8 *)extSmartLog, is_json);
+ }
+
+out:
+ dev_close(dev);
+ if (err > 0)
+ nvme_show_status(err);
+ return err;
+}
+
+static void GetDriveInfo(const char *strOSDirName, int nFD,
+ 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("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)
+{
+ __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 *dir, struct nvme_id_ctrl *ctrlp)
+{
+ WriteData((__u8*)ctrlp, sizeof(*ctrlp), dir,
+ "nvme_controller_identify_data.bin", "id-ctrl");
+}
+
+static void GetSmartlogData(int fd, const char *dir)
+{
+ struct nvme_smart_log smart_log;
+ if (nvme_get_log_smart(fd, -1, false, &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 *dir)
+{
+ 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_get_log_error(fd, entries, false, error_log) == 0) {
+ WriteData((__u8*)error_log, logSize, dir,
+ "error_information_log.bin", "error log");
+ }
+
+ free(error_log);
+}
+
+static void GetGenericLogs(int fd, const char *dir)
+{
+ struct nvme_self_test_log self_test_log;
+ struct nvme_firmware_slot fw_log;
+ struct nvme_cmd_effects_log effects;
+ struct nvme_persistent_event_log pevent_log;
+ void *pevent_log_info = NULL;
+ __u32 log_len = 0;
+ int err = 0 ;
+ bool huge = false;
+
+ /* get self test log */
+ if (nvme_get_log_device_self_test(fd, &self_test_log) == 0) {
+ WriteData((__u8*)&self_test_log, sizeof(self_test_log), dir,
+ "drive_self_test.bin", "self test log");
+ }
+
+ /* get fw slot info log */
+ if (nvme_get_log_fw_slot(fd, false, &fw_log) == 0) {
+ WriteData((__u8*)&fw_log, sizeof(fw_log), dir,
+ "firmware_slot_info_log.bin", "firmware log");
+ }
+
+ /* get effects log */
+ if (nvme_get_log_cmd_effects(fd, NVME_CSI_NVM, &effects) == 0) {
+ WriteData((__u8*)&effects, sizeof(effects), dir,
+ "command_effects_log.bin", "effects log");
+ }
+
+ /* get persistent event log */
+ (void)nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_RELEASE_CTX,
+ sizeof(pevent_log), &pevent_log);
+ memset(&pevent_log, 0, sizeof(pevent_log));
+ err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_EST_CTX_AND_READ,
+ sizeof(pevent_log), &pevent_log);
+ if (err) {
+ fprintf(stderr, "Setting persistent event log read ctx failed (ignored)!\n");
+ return;
+ }
+
+ log_len = le64_to_cpu(pevent_log.tll);
+ pevent_log_info = nvme_alloc(log_len, &huge);
+ if (!pevent_log_info) {
+ perror("could not alloc buffer for persistent event log page (ignored)!\n");
+ return;
+ }
+ err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_READ,
+ log_len, pevent_log_info);
+ if (err == 0) {
+ WriteData((__u8*)pevent_log_info, log_len, dir,
+ "persistent_event_log.bin", "persistent event log");
+ }
+ nvme_free(pevent_log_info, huge);
+ return;
+}
+
+static void GetNSIDDInfo(int fd, const char *dir, int nsid)
+{
+ char file[PATH_MAX] = { 0 };
+ struct nvme_id_ns ns;
+
+ if (nvme_identify_ns(fd, nsid, &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];
+ 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+");
+ if (NULL != fpOSConfig) {
+ fprintf(fpOSConfig,
+ "\n\n\n\n%s\n-----------------------------------------------\n",
+ cmdArray[i].strcmdHeader);
+ fclose(fpOSConfig);
+ fpOSConfig = NULL;
+ }
+ snprintf(strBuffer, sizeof(strBuffer) - 1,
+ cmdArray[i].strCommand, strFileName);
+ if (system(strBuffer))
+ fprintf(stderr, "Failed to send \"%s\"\n", strBuffer);
+ }
+}
+
+static int micron_telemetry_log(int fd, __u8 type, __u8 **data,
+ int *logSize, int da)
+{
+ int err, bs = 512, offset = bs;
+ unsigned short data_area[4];
+ unsigned char ctrl_init = (type == 0x8);
+
+ __u8 *buffer = (unsigned char *)calloc(bs, 1);
+ if (buffer == NULL)
+ return -1;
+ if (ctrl_init)
+ err = nvme_get_log_telemetry_ctrl(fd, true, 0, bs, buffer);
+ else
+ err = nvme_get_log_telemetry_host(fd, 0, bs, buffer);
+ 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] << 8 | buffer[8];
+ data_area[2] = buffer[11] << 8 | buffer[10];
+ data_area[3] = buffer[13] << 8 | 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] * bs;
+ offset = bs;
+ err = 0;
+ if ((buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize))) != NULL) {
+ while (err == 0 && offset != *logSize) {
+ if (ctrl_init)
+ err = nvme_get_log_telemetry_ctrl(fd, true, 0, *logSize, buffer + offset);
+ else
+ err = nvme_get_log_telemetry_host(fd, 0, *logSize, buffer + offset);
+ offset += bs;
+ }
+ }
+
+ 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, "nvmetelemetrylog.bin"},
+ {0x08, "nvmetelemetrylog.bin"},
+ };
+
+ for(i = 0; i < (int)(sizeof(tmap)/sizeof(tmap[0])); i++) {
+ err = micron_telemetry_log(fd, 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) {
+ 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;
+ }
+
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .fid = fmap[i].id,
+ .nsid = 1,
+ .sel = 0,
+ .cdw11 = 0x0,
+ .uuidx = 0,
+ .data_len = len,
+ .data = bufp,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &attrVal,
+ };
+ err = nvme_get_features(&args);
+ 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 {
+ fprintf(stderr, "Feature 0x%x data not retrieved, error %d (ignored)!\n",
+ fmap[i].id, err);
+ 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";
+ struct nvme_id_ctrl ctrl = { 0 };
+ struct nvme_passthru_cmd admin_cmd = { 0 };
+ struct fb_drive_info {
+ unsigned char hw_ver_major;
+ unsigned char hw_ver_minor;
+ unsigned char ftl_unit_size;
+ unsigned char bs_ver_major;
+ unsigned char bs_ver_minor;
+ } dinfo = { 0 };
+ eDriveModel model = UNKNOWN_MODEL;
+ bool is_json = false;
+ struct json_object *root, *driveInfo;
+ struct nvme_dev *dev;
+ struct format {
+ char *fmt;
+ };
+ int err = 0;
+
+ const char *fmt = "output format normal";
+ struct format cfg = {
+ .fmt = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("format", 'f', &cfg.fmt, fmt),
+ OPT_END()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return err;
+
+ if (model == UNKNOWN_MODEL) {
+ fprintf(stderr, "ERROR : Unsupported drive for vs-drive-info cmd");
+ dev_close(dev);
+ return -1;
+ }
+
+ if (strcmp(cfg.fmt, "json") == 0)
+ is_json = true;
+
+ if (model == M5407) {
+ admin_cmd.opcode = 0xD4,
+ admin_cmd.addr = (__u64) (uintptr_t) &dinfo;
+ admin_cmd.data_len = (__u32)sizeof(dinfo);
+ admin_cmd.cdw12 = 3;
+ err = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+ if (err) {
+ fprintf(stderr, "ERROR : drive-info opcode failed with 0x%x\n", err);
+ dev_close(dev);
+ return -1;
+ }
+ } else {
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (err) {
+ fprintf(stderr, "ERROR : identify_ctrl() failed with 0x%x\n", err);
+ dev_close(dev);
+ return -1;
+ }
+ dinfo.hw_ver_major = ctrl.vs[820];
+ dinfo.hw_ver_minor = ctrl.vs[821];
+ dinfo.ftl_unit_size = ctrl.vs[822];
+ }
+
+ if (is_json) {
+ struct json_object *pinfo = json_create_object();
+ char tempstr[64] = { 0 };
+ root = json_create_object();
+ driveInfo = json_create_array();
+ json_object_add_value_array(root, "Micron Drive HW Information", driveInfo);
+ sprintf(tempstr, "%hhu.%hhu", dinfo.hw_ver_major, dinfo.hw_ver_minor);
+ json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr);
+
+ if (dinfo.ftl_unit_size) {
+ sprintf(tempstr, "%hhu KB", dinfo.ftl_unit_size);
+ json_object_add_value_string(pinfo, "FTL_unit_size", tempstr);
+ }
+
+ if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
+ sprintf(tempstr, "%hhu.%hhu", dinfo.bs_ver_major, dinfo.bs_ver_minor);
+ json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr);
+ }
+
+ json_array_add_value_object(driveInfo, pinfo);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ } else {
+ printf("Drive Hardware Version: %hhu.%hhu\n",
+ dinfo.hw_ver_major, dinfo.hw_ver_minor);
+
+ if (dinfo.ftl_unit_size)
+ printf("FTL_unit_size: %hhu KB\n", dinfo.ftl_unit_size);
+
+ if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
+ printf("Boot Spec.Version: %hhu.%hhu\n",
+ dinfo.bs_ver_major, dinfo.bs_ver_minor);
+ }
+ }
+
+ dev_close(dev);
+ 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;
+}
+
+/* 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 On | Power | Previous | New FW | Slot | Commit | Result \n\
+Activation| 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->version != 2) || 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_dev *dev;
+ struct format {
+ char *fmt;
+ };
+ int err;
+
+ const char *fmt = "output format normal";
+ struct format cfg = {
+ .fmt = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("format", 'f', &cfg.fmt, fmt),
+ OPT_END()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel);
+ if (err < 0)
+ return -1;
+
+ if (strcmp(cfg.fmt, "normal") != 0) {
+ fprintf (stderr, "only normal format is supported currently\n");
+ dev_close(dev);
+ return -1;
+ }
+
+ /* check if product supports fw_history log */
+ err = -EINVAL;
+ if (eModel != M51CX) {
+ fprintf(stderr, "Unsupported drive model for vs-fw-activate-history command\n");
+ goto out;
+ }
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xC2, 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->log_page != 0xC2 || (table->version != 2 && table->version != 1))
+ {
+ 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:
+ dev_close(dev);
+ return err;
+}
+
+#define MICRON_FID_LATENCY_MONITOR 0xD0
+#define MICRON_LOG_LATENCY_MONITOR 0xD1
+
+static int micron_latency_stats_track(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = 0;
+ __u32 result = 0;
+ const char *desc = "Enable, Disable or Get cmd latency monitoring stats";
+ const char *option = "enable or disable or status, default is status";
+ const char *command = "commands to monitor for - all|read|write|trim,"
+ " default is all i.e, enabled for all commands";
+ const char *thrtime = "The threshold value to use for latency monitoring in"
+ " milliseconds, default is 800ms";
+
+ int fid = MICRON_FID_LATENCY_MONITOR;
+ eDriveModel model = UNKNOWN_MODEL;
+ uint32_t command_mask = 0x7; /* 1:read 2:write 4:trim 7:all */
+ uint32_t timing_mask = 0x08080800; /* R[31-24]:W[23:16]:T[15:8]:0 */
+ uint32_t enable = 2;
+ struct nvme_dev *dev;
+ struct {
+ char *option;
+ char *command;
+ uint32_t threshold;
+ } opt = {
+ .option = "status",
+ .command = "all",
+ .threshold = 0
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("option", 'o', "option", &opt.option, option),
+ OPT_STRING("command", 'c', "command", &opt.command, command),
+ OPT_UINT("threshold", 't', &opt.threshold, thrtime),
+ OPT_END()
+ };
+
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return -1;
+
+ if (!strcmp(opt.option, "enable")) {
+ enable = 1;
+ } else if (!strcmp(opt.option, "disable")) {
+ enable = 0;
+ } else if (strcmp(opt.option, "status")) {
+ printf("Invalid control option %s specified\n", opt.option);
+ dev_close(dev);
+ return -1;
+ }
+
+ struct nvme_get_features_args g_args = {
+ .args_size = sizeof(g_args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = 0,
+ .sel = 0,
+ .cdw11 = 0,
+ .uuidx = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_get_features(&g_args);
+ if (err != 0) {
+ printf("Failed to retrieve latency monitoring feature status\n");
+ dev_close(dev);
+ return err;
+ }
+
+ /* If it is to retrieve the status only */
+ if (enable == 2) {
+ printf("Latency Tracking Statistics is currently %s",
+ (result & 0xFFFF0000) ? "enabled" : "disabled");
+ if ((result & 7) == 7) {
+ printf(" for All commands\n");
+ } else if ((result & 7) > 0) {
+ printf(" for");
+ if (result & 1) {
+ printf(" Read");
+ }
+ if (result & 2) {
+ printf(" Write");
+ }
+ if (result & 4) {
+ printf(" Trim");
+ }
+ printf(" commands\n");
+ } else if (result == 0) {
+ printf("\n");
+ }
+ dev_close(dev);
+ return err;
+ }
+
+ /* read and validate threshold values if enable option is specified */
+ if (enable == 1) {
+ if (opt.threshold > 2550) {
+ printf("The maximum threshold value cannot be more than 2550 ms\n");
+ dev_close(dev);
+ return -1;
+ }
+ /* timing mask is in terms of 10ms units, so min allowed is 10ms */
+ else if ((opt.threshold % 10) != 0) {
+ printf("The threshold value should be multiple of 10 ms\n");
+ dev_close(dev);
+ return -1;
+ }
+ opt.threshold /= 10;
+ }
+
+ /* read-in command(s) to be monitored */
+ if (!strcmp(opt.command, "read")) {
+ command_mask = 0x1;
+ timing_mask = (opt.threshold << 24);
+ } else if (!strcmp(opt.command, "write")) {
+ command_mask = 0x2;
+ timing_mask = (opt.threshold << 16);
+ } else if (!strcmp(opt.command, "trim")) {
+ command_mask = 0x4;
+ timing_mask = (opt.threshold << 8);
+ } else if (strcmp(opt.command, "all")) {
+ printf("Invalid command %s specified for option %s\n",
+ opt.command, opt.option);
+ dev_close(dev);
+ return -1;
+ }
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = MICRON_FID_LATENCY_MONITOR,
+ .nsid = 0,
+ .cdw11 = enable,
+ .cdw12 = command_mask,
+ .save = 1,
+ .uuidx = 0,
+ .cdw13 = timing_mask,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ if (err == 0) {
+ printf("Successfully %sd latency monitoring for %s commands with %dms threshold\n",
+ opt.option, opt.command, opt.threshold == 0 ? 800 : opt.threshold * 10);
+ } else {
+ printf("Failed to %s latency monitoring for %s commands with %dms threshold\n",
+ opt.option, opt.command, opt.threshold == 0 ? 800 : opt.threshold * 10);
+ }
+
+ dev_close(dev);
+ return err;
+}
+
+
+static int micron_latency_stats_logs(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+#define LATENCY_LOG_ENTRIES 16
+ struct latency_log_entry {
+ uint64_t timestamp;
+ uint32_t latency;
+ uint32_t cmdtag;
+ union {
+ struct {
+ uint32_t opcode:8;
+ uint32_t fuse:2;
+ uint32_t rsvd1:4;
+ uint32_t psdt:2;
+ uint32_t cid:16;
+ };
+ uint32_t dw0;
+ };
+ uint32_t nsid;
+ uint32_t slba_low;
+ uint32_t slba_high;
+ union {
+ struct {
+ uint32_t nlb:16;
+ uint32_t rsvd2:9;
+ uint32_t deac:1;
+ uint32_t prinfo:4;
+ uint32_t fua:1;
+ uint32_t lr:1;
+ };
+ uint32_t dw12;
+ };
+ uint32_t dsm;
+ uint32_t rfu[6];
+ } log[LATENCY_LOG_ENTRIES];
+ eDriveModel model = UNKNOWN_MODEL;
+ struct nvme_dev *dev;
+ int err = -1;
+ const char *desc = "Display Latency tracking log information";
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err)
+ return err;
+ memset(&log, 0, sizeof(log));
+ err = nvme_get_log_simple(dev_fd(dev), 0xD1, sizeof(log), &log);
+ if (err) {
+ if (err < 0)
+ printf("Unable to retrieve latency stats log the drive\n");
+ dev_close(dev);
+ return err;
+ }
+ /* print header and each log entry */
+ printf("Timestamp, Latency, CmdTag, Opcode, Fuse, Psdt,Cid, Nsid,"
+ "Slba_L, Slba_H, Nlb, DEAC, PRINFO, FUA,LR\n");
+ for (int i = 0; i < LATENCY_LOG_ENTRIES; i++) {
+ printf("%"PRIu64",%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n",
+ log[i].timestamp,log[i].latency, log[i].cmdtag, log[i].opcode,
+ log[i].fuse, log[i].psdt, log[i].cid, log[i].nsid,
+ log[i].slba_low, log[i].slba_high, log[i].nlb,
+ log[i].deac, log[i].prinfo, log[i].fua, log[i].lr);
+ }
+ printf("\n");
+ dev_close(dev);
+ return err;
+}
+
+static int micron_latency_stats_info(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "display command latency statistics";
+ const char *command = "command to display stats - all|read|write|trim"
+ "default is all";
+ int err = 0;
+ struct nvme_dev *dev;
+ eDriveModel model = UNKNOWN_MODEL;
+ #define LATENCY_BUCKET_COUNT 32
+ #define LATENCY_BUCKET_RSVD 32
+ struct micron_latency_stats {
+ uint64_t version; /* major << 32 | minior */
+ uint64_t all_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD];
+ uint64_t read_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD];
+ uint64_t write_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD];
+ uint64_t trim_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD];
+ uint32_t reserved[255]; /* round up to 4K */
+ } log;
+
+ struct latency_thresholds {
+ uint32_t start;
+ uint32_t end;
+ char *unit;
+ } thresholds[LATENCY_BUCKET_COUNT] = {
+ {0, 50, "us"}, {50, 100, "us"}, {100, 150, "us"}, {150, 200, "us"},
+ {200, 300, "us"}, {300, 400, "us"}, {400, 500, "us"}, {500, 600, "us"},
+ {600, 700, "us"}, {700, 800, "us"}, {800, 900, "us"}, {900, 1000, "us"},
+ {1, 5, "ms"}, {5, 10, "ms"}, {10, 20, "ms"}, {20, 50, "ms"}, {50, 100, "ms"},
+ {100, 200, "ms"}, {200, 300, "ms"}, {300, 400, "ms"}, {400, 500, "ms"},
+ {500, 600, "ms"}, {600, 700, "ms"}, {700, 800, "ms"}, {800, 900, "ms"},
+ {900, 1000, "ms"}, {1, 2, "s"}, {2, 3, "s"}, {3, 4, "s"}, {4, 5, "s"},
+ {5,8, "s"},
+ {8, INT_MAX, "s"},
+ };
+
+ struct {
+ char *command;
+ } opt = {
+ .command="all"
+ };
+
+ uint64_t *cmd_stats = &log.all_cmds[0];
+ char *cmd_str = "All";
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("command", 'c', "command", &opt.command, command),
+ OPT_END()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return err;
+ if (!strcmp(opt.command, "read")) {
+ cmd_stats = &log.read_cmds[0];
+ cmd_str = "Read";
+ } else if (!strcmp(opt.command, "write")) {
+ cmd_stats = &log.write_cmds[0];
+ cmd_str = "Write";
+ } else if (!strcmp(opt.command, "trim")) {
+ cmd_stats = &log.trim_cmds[0];
+ cmd_str = "Trim";
+ } else if (strcmp(opt.command, "all")) {
+ printf("Invalid command option %s to display latency stats\n", opt.command);
+ dev_close(dev);
+ return -1;
+ }
+
+ memset(&log, 0, sizeof(log));
+ err = nvme_get_log_simple(dev_fd(dev), 0xD0, sizeof(log), &log);
+ if (err) {
+ if (err < 0)
+ printf("Unable to retrieve latency stats log the drive\n");
+ dev_close(dev);
+ return err;
+ }
+ printf("Micron IO %s Command Latency Statistics\n"
+ "Major Revision : %d\nMinor Revision : %d\n",
+ cmd_str, (int)(log.version >> 32), (int)(log.version & 0xFFFFFFFF));
+ printf("=============================================\n");
+ printf("Bucket Start End Command Count\n");
+ printf("=============================================\n");
+
+ for (int b = 0; b < LATENCY_BUCKET_COUNT; b++) {
+ int bucket = b + 1;
+ char start[32] = { 0 };
+ char end[32] = { 0 };
+ sprintf(start, "%u%s", thresholds[b].start, thresholds[b].unit);
+ if (thresholds[b].end == INT_MAX)
+ sprintf(end, "INF");
+ else
+ sprintf(end, "%u%s", thresholds[b].end, thresholds[b].unit);
+ printf("%2d %8s %8s %8"PRIu64"\n",
+ bucket, start, end, cmd_stats[b]);
+ }
+ dev_close(dev);
+ return err;
+}
+
+static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve Smart or Extended Smart Health log for the given device ";
+ unsigned int logC0[C0_log_size/sizeof(int)] = { 0 };
+ unsigned int logFB[FB_log_size/sizeof(int)] = { 0 };
+ struct nvme_id_ctrl ctrl;
+ eDriveModel eModel = UNKNOWN_MODEL;
+ struct nvme_dev *dev;
+ bool is_json = true;
+ struct format {
+ char *fmt;
+ };
+ const char *fmt = "output format normal|json";
+ struct format cfg = {
+ .fmt = "json",
+ };
+ int err = 0;
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("format", 'f', &cfg.fmt, fmt),
+ OPT_END()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel);
+ if (err < 0)
+ return -1;
+
+ if (strcmp(cfg.fmt, "normal") == 0)
+ is_json = false;
+
+ /* For M5410 and M5407, this option prints 0xFB log page */
+ if (eModel == M5410 || eModel == M5407) {
+ __u8 spec = (eModel == M5410) ? 0 : 1;
+ __u8 nsze;
+
+ if ((err = nvme_identify_ctrl(dev_fd(dev), &ctrl)) == 0)
+ err = nvme_get_log_simple(dev_fd(dev), 0xFB,
+ FB_log_size, logFB);
+ if (err) {
+ if (err < 0)
+ printf("Unable to retrieve smart log 0xFB for the drive\n");
+ goto out;
+ }
+
+ nsze = (ctrl.vs[987] == 0x12);
+ if (nsze == 0 && nsze_from_oacs)
+ nsze = ((ctrl.oacs >> 3) & 0x1);
+ print_nand_stats_fb((__u8 *)logFB, NULL, nsze, is_json, spec);
+ goto out;
+ }
+
+ /* check for models that support 0xC0 log */
+ if (eModel != M51CX) {
+ printf ("Unsupported drive model for vs-smart-add-log commmand\n");
+ err = -1;
+ goto out;
+ }
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0);
+ if (err == 0) {
+ print_smart_cloud_health_log((__u8 *)logC0, is_json);
+ } else if (err < 0) {
+ printf("Unable to retrieve extended smart log 0xC0 for the drive\n");
+ }
+out:
+ dev_close(dev);
+ if (err > 0)
+ nvme_show_status(err);
+ 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";
+ __u32 result = 0;
+ __u8 fid = MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY;
+ eDriveModel model = UNKNOWN_MODEL;
+ struct nvme_dev *dev;
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+ int err = 0;
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return err;
+
+ if (model != M51CX) {
+ printf ("This option is not supported for specified drive\n");
+ dev_close(dev);
+ return err;
+ }
+
+ err = nvme_set_features_simple(dev_fd(dev), fid, 1 << 31, 0, 0, &result);
+ if (err == 0) err = (int)result;
+ else printf ("Failed to clear fw activation history, error = 0x%x\n", err);
+
+ dev_close(dev);
+ 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 fid = MICRON_FEATURE_TELEMETRY_CONTROL_OPTION;
+ eDriveModel model = UNKNOWN_MODEL;
+ struct nvme_id_ctrl ctrl = { 0 };
+ struct nvme_dev *dev;
+
+ 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()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return -1;
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if ((ctrl.lpa & 0x8) != 0x8) {
+ printf("drive doesn't support host/controller generated telemetry logs\n");
+ dev_close(dev);
+ return err;
+ }
+
+ if (!strcmp(opt.option, "enable")) {
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = 1,
+ .cdw11 = 1,
+ .cdw12 = 0,
+ .save = (opt.select & 0x1),
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ 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")) {
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = 1,
+ .cdw11 = 0,
+ .cdw12 = 0,
+ .save = (opt.select & 0x1),
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ 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")) {
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = fid,
+ .nsid = 1,
+ .sel = opt.select & 0x3,
+ .cdw11 = 0,
+ .uuidx = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_get_features(&args);
+ 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);
+ dev_close(dev);
+ return -1;
+ }
+
+ dev_close(dev);
+ return err;
+}
+
+/* M51XX models log page header */
+struct micron_common_log_header {
+ uint8_t id;
+ uint8_t version;
+ uint16_t pn;
+ uint32_t log_size;
+ uint32_t max_size;
+ uint32_t write_pointer;
+ uint32_t next_pointer;
+ uint32_t overwritten_bytes;
+ uint8_t flags;
+ uint8_t reserved[7];
+};
+
+/* helper function to retrieve logs with specific offset and max chunk size */
+int nvme_get_log_lpo(int fd, __u8 log_id, __u32 lpo, __u32 chunk,
+ __u32 data_len, void *data)
+{
+ __u32 offset = lpo, xfer_len = data_len;
+ void *ptr = data;
+ struct nvme_get_log_args args = {
+ .lpo = offset,
+ .result = NULL,
+ .log = ptr,
+ .args_size = sizeof(args),
+ .fd = fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = log_id,
+ .len = xfer_len,
+ .nsid = NVME_NSID_ALL,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = NVME_LOG_LSP_NONE,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+ int ret = 0;
+
+ /* divide data into multiple chunks */
+ do {
+ xfer_len = data_len - offset;
+ if (xfer_len > chunk)
+ xfer_len = chunk;
+
+ args.lpo = offset;
+ args.log = ptr;
+ args.len = xfer_len;
+ ret = nvme_get_log(&args);
+ if (ret)
+ return ret;
+ offset += xfer_len;
+ ptr += xfer_len;
+ } while (offset < data_len);
+ return ret;
+}
+
+/* retrieves logs with common log format */
+static int get_common_log(int fd, uint8_t id, uint8_t **buf, int *size)
+{
+ struct micron_common_log_header hdr = { 0 };
+ int log_size = sizeof(hdr), first = 0, second = 0;
+ uint8_t *buffer = NULL;
+ int ret = -1;
+ int chunk = 0x4000; /* max chunk size to be used for these logs */
+
+ ret = nvme_get_log_simple(fd, id, sizeof(hdr), &hdr);
+ if (ret) {
+ fprintf(stderr, "pull hdr failed for %hhu with error: 0x%x\n", id, ret);
+ return ret;
+ }
+
+ if (hdr.id != id ||
+ hdr.log_size == 0 ||
+ hdr.max_size == 0 ||
+ hdr.write_pointer < sizeof(hdr))
+ {
+ fprintf(stderr, "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, "
+ "max: %u, wp: %u, flags: %hhu, np: %u\n", id,
+ hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer,
+ hdr.flags, hdr.next_pointer);
+ return 1;
+ }
+
+ /* we may have just 32-bytes for some models; write to wfile if log hasn't
+ * yet reached its max size
+ */
+ if (hdr.log_size == sizeof(hdr)) {
+ buffer = (uint8_t *)malloc(sizeof(hdr));
+ if (buffer == NULL) {
+ fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n",
+ sizeof(hdr), id);
+ return -ENOMEM;
+ }
+ memcpy(buffer,(uint8_t *)&hdr, sizeof(hdr));
+ } else if (hdr.log_size < hdr.max_size) {
+ buffer = (uint8_t *)malloc(sizeof(hdr) + hdr.log_size);
+ if (buffer == NULL) {
+ fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n",
+ hdr.log_size + sizeof(hdr), id);
+ return -ENOMEM;
+ }
+ memcpy(buffer, &hdr, sizeof(hdr));
+ ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, hdr.log_size,
+ buffer + sizeof(hdr));
+ if (ret == 0) {
+ log_size += hdr.log_size;
+ }
+ } else if (hdr.log_size >= hdr.max_size) {
+ /* reached maximum, to maintain, sequence we need to depend on write
+ * pointer to detect wrap-overs. FW doesn't yet implement the condition
+ * hdr.log_size > hdr.max_size; also ignore over-written log data; we
+ * also ignore collisions for now
+ */
+ buffer = (uint8_t *)malloc(hdr.max_size + sizeof(hdr));
+ if (buffer == NULL) {
+ fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n",
+ hdr.max_size + sizeof(hdr), id);
+ return -ENOMEM;
+ }
+ memcpy(buffer, &hdr, sizeof(hdr));
+
+ first = hdr.max_size - hdr.write_pointer;
+ second = hdr.write_pointer - sizeof(hdr);
+
+ if (first) {
+ ret = nvme_get_log_lpo(fd, id, hdr.write_pointer, chunk, first,
+ buffer + sizeof(hdr));
+ if (ret) {
+ free(buffer);
+ fprintf(stderr, "failed to get log: 0x%X\n", id);
+ return ret;
+ }
+ log_size += first;
+ }
+ if (second) {
+ ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, second,
+ buffer + sizeof(hdr) + first);
+ if (ret) {
+ fprintf(stderr, "failed to get log: 0x%X\n", id);
+ free(buffer);
+ return ret;
+ }
+ log_size += second;
+ }
+ }
+ *buf = buffer;
+ *size = log_size;
+ return ret;
+}
+
+static int micron_internal_logs(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = -EINVAL;
+ 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 };
+ int c_logs_index = 8; /* should be current size of aVendorLogs */
+ struct nvme_dev *dev;
+ 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 }
+ },
+ aM51CXLogs[] = {
+ { 0xE1, "nvmelog_E1.bin", 0, 0 },
+ { 0xE2, "nvmelog_E2.bin", 0, 0 },
+ { 0xE3, "nvmelog_E3.bin", 0, 0 },
+ { 0xE4, "nvmelog_E4.bin", 0, 0 },
+ { 0xE5, "nvmelog_E5.bin", 0, 0 },
+ { 0xE8, "nvmelog_E8.bin", 0, 0 },
+ { 0xE9, "nvmelog_E9.bin", 0, 0 },
+ { 0xEA, "nvmelog_EA.bin", 0, 0 },
+ };
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ /* 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");
+ 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");
+ 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");
+ 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");
+ goto out;
+ }
+
+ err = nvme_identify_ctrl(dev_fd(dev), &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");
+ goto out;
+ }
+ int logSize = 0; __u8 *buffer = NULL; const char *dir = ".";
+ err = micron_telemetry_log(dev_fd(dev), 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);
+ }
+ 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((int)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(dev_fd(dev), strCtrlDirName, i);
+
+ GetSmartlogData(dev_fd(dev), strCtrlDirName);
+ GetErrorlogData(dev_fd(dev), ctrl.elpe, strCtrlDirName);
+ GetGenericLogs(dev_fd(dev), strCtrlDirName);
+ /* pull if telemetry log data is supported */
+ if ((ctrl.lpa & 0x8) == 0x8)
+ GetTelemetryData(dev_fd(dev), strCtrlDirName);
+
+ GetFeatureSettings(dev_fd(dev), strCtrlDirName);
+
+ if (eModel != M5410 && eModel != M5407) {
+ memcpy(&aVendorLogs[c_logs_index], aM51XXLogs, sizeof(aM51XXLogs));
+ c_logs_index += sizeof(aM51XXLogs)/sizeof(aM51XXLogs[0]);
+ if (eModel == M51AX)
+ memcpy((char *)&aVendorLogs[c_logs_index], aM51AXLogs, sizeof(aM51AXLogs));
+ else if (eModel == M51BX)
+ memcpy((char *)&aVendorLogs[c_logs_index], aM51BXLogs, sizeof(aM51BXLogs));
+ else if (eModel == M51CX)
+ memcpy((char *)&aVendorLogs[c_logs_index], aM51CXLogs, sizeof(aM51CXLogs));
+ }
+
+ for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) &&
+ aVendorLogs[i].ucLogPage != 0; i++) {
+ err = -1;
+ switch (aVendorLogs[i].ucLogPage) {
+ case 0xE1:
+ case 0xE5:
+ case 0xE9:
+ err = 1;
+ break;
+
+ case 0xE2:
+ case 0xE3:
+ case 0xE4:
+ case 0xE8:
+ case 0xEA:
+ err = get_common_log(dev_fd(dev), aVendorLogs[i].ucLogPage,
+ &dataBuffer, &bSize);
+ break;
+
+ case 0xC1:
+ case 0xC2:
+ case 0xC4:
+ err = GetLogPageSize(dev_fd(dev), aVendorLogs[i].ucLogPage,
+ &bSize);
+ if (err == 0 && bSize > 0)
+ err = GetCommonLogPage(dev_fd(dev), 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(dev_fd(dev),
+ aVendorLogs[i].ucLogPage, dataBuffer,
+ bSize);
+ else
+ err = nvme_get_log_simple(dev_fd(dev),
+ aVendorLogs[i].ucLogPage,
+ bSize, dataBuffer);
+ }
+ break;
+
+ case 0xF7:
+ case 0xF9:
+ case 0xFC:
+ case 0xFD:
+ if (eModel == M51BX) {
+ (void)NVMEResetLog(dev_fd(dev), aVendorLogs[i].ucLogPage,
+ aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize);
+ }
+ /* fallthrough */
+ default:
+ bSize = aVendorLogs[i].nLogSize;
+ dataBuffer = (unsigned char *)malloc(bSize);
+ if (dataBuffer == NULL) {
+ break;
+ }
+ memset(dataBuffer, 0, bSize);
+ err = nvme_get_log_simple(dev_fd(dev), aVendorLogs[i].ucLogPage,
+ 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_simple(dev_fd(dev),
+ aVendorLogs[i].ucLogPage,
+ 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:
+ dev_close(dev);
+ return err;
+}
+
+#define MIN_LOG_SIZE 512
+static int micron_logpage_dir(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = -1;
+ const char *desc = "List the supported log pages";
+ eDriveModel model = UNKNOWN_MODEL;
+ char logbuf[MIN_LOG_SIZE];
+ struct nvme_dev *dev;
+ int i;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = micron_parse_options(&dev, argc, argv, desc, opts, &model);
+ if (err < 0)
+ return err;
+
+ struct nvme_supported_logs {
+ uint8_t log_id;
+ uint8_t supported;
+ char *desc;
+ } log_list[] = {
+ {0x00, 0, "Support Log Pages"},
+ {0x01, 0, "Error Information"},
+ {0x02, 0, "SMART / Health Information"},
+ {0x03, 0, "Firmware Slot Information"},
+ {0x04, 0, "Changed Namespace List"},
+ {0x05, 0, "Commands Supported and Effects"},
+ {0x06, 0, "Device Self Test"},
+ {0x07, 0, "Telemetry Host-Initiated"},
+ {0x08, 0, "Telemetry Controller-Initiated"},
+ {0x09, 0, "Endurance Group Information"},
+ {0x0A, 0, "Predictable Latency Per NVM Set"},
+ {0x0B, 0, "Predictable Latency Event Aggregate"},
+ {0x0C, 0, "Asymmetric Namespace Access"},
+ {0x0D, 0, "Persistent Event Log"},
+ {0x0E, 0, "Predictable Latency Event Aggregate"},
+ {0x0F, 0, "Endurance Group Event Aggregate"},
+ {0x10, 0, "Media Unit Status"},
+ {0x11, 0, "Supported Capacity Configuration List"},
+ {0x12, 0, "Feature Identifiers Supported and Effects"},
+ {0x13, 0, "NVMe-MI Commands Supported and Effects"},
+ {0x14, 0, "Command and Feature lockdown"},
+ {0x15, 0, "Boot Partition"},
+ {0x16, 0, "Rotational Media Information"},
+ {0x70, 0, "Discovery"},
+ {0x80, 0, "Reservation Notification"},
+ {0x81, 0, "Sanitize Status"},
+ {0xC0, 0, "SMART Cloud Health Log"},
+ {0xC2, 0, "Firmware Activation History"},
+ {0xC3, 0, "Latency Monitor Log"},
+ };
+
+ printf("Supported log page list\nLog ID : Description\n");
+ for (i = 0; i < sizeof(log_list)/sizeof(log_list[0]); i++) {
+ err = nvme_get_log_simple(dev_fd(dev), log_list[i].log_id,
+ MIN_LOG_SIZE, &logbuf[0]);
+ if (err) continue;
+ printf("%02Xh : %s\n", log_list[i].log_id, log_list[i].desc);
+ }
+
+ return err;
+}
diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h
new file mode 100644
index 0000000..4f7b892
--- /dev/null
+++ b/plugins/micron/micron-nvme.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/micron/micron-nvme
+
+#if !defined(MICRON_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define MICRON_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("micron", "Micron vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("select-download", "Selective Firmware Download", micron_selective_download)
+ ENTRY("vs-temperature-stats", "Retrieve Micron temperature statistics ", micron_temp_stats)
+ 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-smart-ext-log", "Retrieve extended SMART logs", micron_smart_ext_log)
+ 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("latency-tracking", "Latency monitoring feature control", micron_latency_stats_track)
+ ENTRY("latency-stats", "Latency information for tracked commands", micron_latency_stats_info)
+ ENTRY("latency-logs", "Latency log details tracked by drive", micron_latency_stats_logs)
+ 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)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/netapp/netapp-nvme.c b/plugins/netapp/netapp-nvme.c
new file mode 100644
index 0000000..f5cb073
--- /dev/null
+++ b/plugins/netapp/netapp-nvme.c
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+* Copyright (c) 2018 NetApp, Inc.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+*/
+
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+
+#include "util/suffix.h"
+
+#define CREATE_CMD
+#include "netapp-nvme.h"
+
+#define ONTAP_C2_LOG_ID 0xC2
+#define ONTAP_C2_LOG_SIZE 4096
+#define ONTAP_LABEL_LEN 260
+#define ONTAP_NS_PATHLEN 525
+
+enum {
+ NNORMAL,
+ NJSON,
+ NCOLUMN,
+};
+
+enum {
+ ONTAP_C2_LOG_SUPPORTED_LSP = 0x0,
+ ONTAP_C2_LOG_NSINFO_LSP = 0x1,
+};
+
+enum {
+ ONTAP_VSERVER_TLV = 0x11,
+ ONTAP_VOLUME_TLV = 0x12,
+ ONTAP_NS_TLV = 0x13,
+};
+
+static const char *dev_path = "/dev/";
+
+struct smdevice_info {
+ unsigned nsid;
+ struct nvme_id_ctrl ctrl;
+ struct nvme_id_ns ns;
+ char dev[265];
+};
+
+struct ontapdevice_info {
+ unsigned nsid;
+ struct nvme_id_ctrl ctrl;
+ struct nvme_id_ns ns;
+ unsigned char uuid[NVME_UUID_LEN];
+ unsigned char log_data[ONTAP_C2_LOG_SIZE];
+ char dev[265];
+};
+
+#define ARRAY_LABEL_LEN 60
+#define VOLUME_LABEL_LEN 60
+
+/*
+ * Format of the string isn't tightly controlled yet. For now, squash UCS-2 into
+ * ASCII. dst buffer must be at least count + 1 bytes long
+ */
+static void netapp_convert_string(char *dst, char *src, unsigned int count)
+{
+ int i;
+
+ if (!dst || !src || !count)
+ return;
+
+ memset(dst, 0, count + 1);
+ for (i = 0; i < count; i++)
+ dst[i] = src[i * 2 + 1];
+ /* the json routines won't accept empty strings */
+ if (strlen(dst) == 0 && count)
+ dst[0] = ' ';
+}
+
+static void netapp_nguid_to_str(char *str, __u8 *nguid)
+{
+ int i;
+
+ memset(str, 0, 33);
+ for (i = 0; i < 16; i++)
+ str += sprintf(str, "%02x", nguid[i]);
+}
+
+static void netapp_get_ns_size(char *size, unsigned long long *lba,
+ struct nvme_id_ns *ns)
+{
+ __u8 lba_index;
+ nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &lba_index);
+ *lba = 1ULL << ns->lbaf[lba_index].ds;
+ double nsze = le64_to_cpu(ns->nsze) * (*lba);
+ const char *s_suffix = suffix_si_get(&nsze);
+
+ sprintf(size, "%.2f%sB", nsze, s_suffix);
+}
+
+static void ontap_labels_to_str(char *dst, char *src, int count)
+{
+ int i;
+
+ memset(dst, 0, ONTAP_LABEL_LEN);
+ for (i = 0; i < count; i++) {
+ if (src[i] >= '!' && src[i] <= '~')
+ dst[i] = src[i];
+ else
+ break;
+ }
+ dst[i] = '\0';
+}
+
+static void netapp_get_ontap_labels(char *vsname, char *nspath,
+ unsigned char *log_data)
+{
+ int lsp, tlv, label_len;
+ char *vserver_name, *volume_name, *namespace_name;
+ char vol_name[ONTAP_LABEL_LEN], ns_name[ONTAP_LABEL_LEN];
+ const char *ontap_vol = "/vol/";
+ int i, j;
+
+ /* get the lsp */
+ lsp = (*(__u8 *)&log_data[16]) & 0x0F;
+ if (lsp != ONTAP_C2_LOG_NSINFO_LSP)
+ /* lsp not related to nsinfo */
+ return;
+
+ /* get the vserver tlv and name */
+ tlv = *(__u8 *)&log_data[32];
+ if (tlv == ONTAP_VSERVER_TLV) {
+ label_len = (*(__u16 *)&log_data[34]) * 4;
+ vserver_name = (char *)&log_data[36];
+ ontap_labels_to_str(vsname, vserver_name, label_len);
+ } else {
+ /* not the expected vserver tlv */
+ fprintf(stderr, "Unable to fetch ONTAP vserver name\n");
+ return;
+ }
+
+ i = 36 + label_len;
+ j = i + 2;
+ /* get the volume tlv and name */
+ tlv = *(__u8 *)&log_data[i];
+ if (tlv == ONTAP_VOLUME_TLV) {
+ label_len = (*(__u16 *)&log_data[j]) * 4;
+ volume_name = (char *)&log_data[j + 2];
+ ontap_labels_to_str(vol_name, volume_name, label_len);
+ } else {
+ /* not the expected volume tlv */
+ fprintf(stderr, "Unable to fetch ONTAP volume name\n");
+ return;
+ }
+
+ i += 4 + label_len;
+ j += 4 + label_len;
+ /* get the namespace tlv and name */
+ tlv = *(__u8 *)&log_data[i];
+ if (tlv == ONTAP_NS_TLV) {
+ label_len = (*(__u16 *)&log_data[j]) * 4;
+ namespace_name = (char *)&log_data[j + 2];
+ ontap_labels_to_str(ns_name, namespace_name, label_len);
+ } else {
+ /* not the expected namespace tlv */
+ fprintf(stderr, "Unable to fetch ONTAP namespace name\n");
+ return;
+ }
+
+ snprintf(nspath, ONTAP_NS_PATHLEN, "%s%s%s%s", ontap_vol,
+ vol_name, "/", ns_name);
+}
+
+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)
+{
+ struct json_object *device_attrs;
+
+ device_attrs = json_create_object();
+ json_object_add_value_string(device_attrs, "Device", devname);
+ json_object_add_value_string(device_attrs, "Array_Name", arrayname);
+ json_object_add_value_string(device_attrs, "Volume_Name", volname);
+ json_object_add_value_int(device_attrs, "NSID", nsid);
+ json_object_add_value_string(device_attrs, "Volume_ID", nguid);
+ json_object_add_value_string(device_attrs, "Controller", ctrl);
+ json_object_add_value_string(device_attrs, "Access_State", astate);
+ json_object_add_value_string(device_attrs, "Size", size);
+ json_object_add_value_int(device_attrs, "LBA_Data_Size", lba);
+ json_object_add_value_int(device_attrs, "Namespace_Size", nsze);
+
+ json_array_add_value_object(devices, device_attrs);
+}
+
+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)
+{
+ struct json_object *device_attrs;
+
+ device_attrs = json_create_object();
+ json_object_add_value_string(device_attrs, "Device", devname);
+ json_object_add_value_string(device_attrs, "Vserver", vsname);
+ json_object_add_value_string(device_attrs, "Namespace_Path", nspath);
+ json_object_add_value_int(device_attrs, "NSID", nsid);
+ json_object_add_value_string(device_attrs, "UUID", uuid);
+ json_object_add_value_string(device_attrs, "Size", size);
+ json_object_add_value_int(device_attrs, "LBA_Data_Size", lba);
+ json_object_add_value_int(device_attrs, "Namespace_Size", nsze);
+
+ json_array_add_value_object(devices, device_attrs);
+}
+
+static void netapp_smdevices_print(struct smdevice_info *devices, int count, int format)
+{
+ struct json_object *root = 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];
+ char nguid_str[33];
+ char basestr[] = "%s, Array Name %s, Volume Name %s, NSID %d, "
+ "Volume ID %s, Controller %c, Access State %s, %s\n";
+ char columnstr[] = "%-16s %-30s %-30s %4d %32s %c %-12s %9s\n";
+ char *formatstr = basestr; /* default to "normal" output format */
+ __u8 lba_index;
+
+ if (format == NCOLUMN) {
+ /* for column output, change output string and print column headers */
+ formatstr = columnstr;
+ printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s\n",
+ "Device", "Array Name", "Volume Name", "NSID",
+ "Volume ID", "Ctrl", "Access State", " Size");
+ printf("%-16s %-30s %-30s %-4s %-32s %-4s %-12s %-9s\n",
+ "----------------", "------------------------------",
+ "------------------------------", "----",
+ "--------------------------------", "----",
+ "------------", "---------");
+ }
+ else if (format == NJSON) {
+ /* prepare for json output */
+ root = json_create_object();
+ json_devices = json_create_array();
+ }
+
+ for (i = 0; i < count; i++) {
+ nvme_id_ns_flbas_to_lbaf_inuse(devices[i].ns.flbas, &lba_index);
+ unsigned long long int lba = 1ULL << devices[i].ns.lbaf[lba_index].ds;
+ double nsze = le64_to_cpu(devices[i].ns.nsze) * lba;
+ const char *s_suffix = suffix_si_get(&nsze);
+ char size[128];
+
+ sprintf(size, "%.2f%sB", nsze, s_suffix);
+ netapp_convert_string(array_label, (char *)&devices[i].ctrl.vs[20],
+ ARRAY_LABEL_LEN / 2);
+ slta = devices[i].ctrl.vs[0] & 0x1;
+ netapp_convert_string(volume_label, (char *)devices[i].ns.vs,
+ VOLUME_LABEL_LEN / 2);
+ netapp_nguid_to_str(nguid_str, devices[i].ns.nguid);
+ if (format == NJSON)
+ netapp_smdevice_json(json_devices, devices[i].dev,
+ array_label, volume_label, devices[i].nsid,
+ nguid_str, slta ? "A" : "B", "unknown", size,
+ lba, le64_to_cpu(devices[i].ns.nsze));
+ else
+ printf(formatstr, devices[i].dev, array_label,
+ volume_label, devices[i].nsid, nguid_str,
+ slta ? 'A' : 'B', "unknown", size);
+ }
+
+ if (format == NJSON) {
+ /* complete the json output */
+ json_object_add_value_array(root, "SMdevices", json_devices);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ }
+}
+
+static void netapp_ontapdevices_print(struct ontapdevice_info *devices,
+ int count, int format)
+{
+ struct json_object *root = NULL;
+ struct json_object *json_devices = NULL;
+ char vsname[ONTAP_LABEL_LEN] = " ";
+ char nspath[ONTAP_NS_PATHLEN] = " ";
+ unsigned long long lba;
+ char size[128];
+ char uuid_str[37] = " ";
+ int i;
+
+ char basestr[] = "%s, Vserver %s, Namespace Path %s, NSID %d, UUID %s, %s\n";
+ char columnstr[] = "%-16s %-25s %-50s %-4d %-38s %-9s\n";
+
+ /* default to 'normal' output format */
+ char *formatstr = basestr;
+
+ if (format == NCOLUMN) {
+ /* change output string and print column headers */
+ formatstr = columnstr;
+ printf("%-16s %-25s %-50s %-4s %-38s %-9s\n",
+ "Device", "Vserver", "Namespace Path",
+ "NSID", "UUID", "Size");
+ printf("%-16s %-25s %-50s %-4s %-38s %-9s\n",
+ "----------------", "-------------------------",
+ "--------------------------------------------------",
+ "----", "--------------------------------------",
+ "---------");
+ } else if (format == NJSON) {
+ /* prepare for json output */
+ root = json_create_object();
+ json_devices = json_create_array();
+ }
+
+ for (i = 0; i < count; i++) {
+
+ netapp_get_ns_size(size, &lba, &devices[i].ns);
+ nvme_uuid_to_string(devices[i].uuid, uuid_str);
+ netapp_get_ontap_labels(vsname, nspath, devices[i].log_data);
+
+ if (format == NJSON) {
+ netapp_ontapdevice_json(json_devices, devices[i].dev,
+ vsname, nspath, devices[i].nsid,
+ uuid_str, size, lba,
+ le64_to_cpu(devices[i].ns.nsze));
+ } else
+ printf(formatstr, devices[i].dev, vsname, nspath,
+ devices[i].nsid, uuid_str, size);
+ }
+
+ if (format == NJSON) {
+ /* complete the json output */
+ json_object_add_value_array(root, "ONTAPdevices", json_devices);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ }
+}
+
+static int nvme_get_ontap_c2_log(int fd, __u32 nsid, void *buf, __u32 buflen)
+{
+ struct nvme_passthru_cmd get_log;
+ int err;
+
+ memset(buf, 0, buflen);
+ memset(&get_log, 0, sizeof(struct nvme_passthru_cmd));
+
+ get_log.opcode = nvme_admin_get_log_page;
+ get_log.nsid = nsid;
+ get_log.addr = (__u64)(uintptr_t)buf;
+ get_log.data_len = buflen;
+
+ __u32 numd = (get_log.data_len >> 2) - 1;
+ __u32 numdu = numd >> 16;
+ __u32 numdl = numd & 0xFFFF;
+
+ get_log.cdw10 = ONTAP_C2_LOG_ID | (numdl << 16);
+ get_log.cdw10 |= ONTAP_C2_LOG_NSINFO_LSP << 8;
+ get_log.cdw11 = numdu;
+
+ err = nvme_submit_admin_passthru(fd, &get_log, NULL);
+ if (err) {
+ fprintf(stderr, "ioctl error %0x\n", err);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int netapp_smdevices_get_info(int fd, struct smdevice_info *item,
+ const char *dev)
+{
+ int err;
+
+ err = nvme_identify_ctrl(fd, &item->ctrl);
+ if (err) {
+ fprintf(stderr,
+ "Identify Controller failed to %s (%s)\n", dev,
+ err < 0 ? strerror(-err) :
+ nvme_status_to_string(err, false));
+ return 0;
+ }
+
+ if (strncmp("NetApp E-Series", item->ctrl.mn, 15) != 0)
+ return 0; /* not the right model of controller */
+
+ err = nvme_get_nsid(fd, &item->nsid);
+ err = nvme_identify_ns(fd, item->nsid, &item->ns);
+ if (err) {
+ fprintf(stderr,
+ "Unable to identify namespace for %s (%s)\n",
+ dev, err < 0 ? strerror(-err) :
+ nvme_status_to_string(err, false));
+ return 0;
+ }
+ strncpy(item->dev, dev, sizeof(item->dev) - 1);
+
+ return 1;
+}
+
+static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_info *item,
+ const char *dev)
+{
+ int err;
+ void *nsdescs;
+
+ err = nvme_identify_ctrl(fd, &item->ctrl);
+ if (err) {
+ fprintf(stderr, "Identify Controller failed to %s (%s)\n",
+ dev, err < 0 ? strerror(-err) :
+ nvme_status_to_string(err, false));
+ return 0;
+ }
+
+ if (strncmp("NetApp ONTAP Controller", item->ctrl.mn, 23) != 0)
+ /* not the right controller model */
+ return 0;
+
+ err = nvme_get_nsid(fd, &item->nsid);
+
+ err = nvme_identify_ns(fd, item->nsid, &item->ns);
+ if (err) {
+ fprintf(stderr, "Unable to identify namespace for %s (%s)\n",
+ dev, err < 0 ? strerror(-err) :
+ nvme_status_to_string(err, false));
+ return 0;
+ }
+
+ if (posix_memalign(&nsdescs, getpagesize(), 0x1000)) {
+ fprintf(stderr, "Cannot allocate controller list payload\n");
+ return 0;
+ }
+
+ err = nvme_identify_ns_descs(fd, item->nsid, nsdescs);
+ if (err) {
+ fprintf(stderr, "Unable to identify namespace descriptor for %s (%s)\n",
+ dev, err < 0 ? strerror(-err) :
+ nvme_status_to_string(err, false));
+ free(nsdescs);
+ return 0;
+ }
+
+ memcpy(item->uuid, nsdescs + sizeof(struct nvme_ns_id_desc), sizeof(item->uuid));
+ free(nsdescs);
+
+ err = nvme_get_ontap_c2_log(fd, item->nsid, item->log_data, ONTAP_C2_LOG_SIZE);
+ if (err) {
+ fprintf(stderr, "Unable to get log page data for %s (%s)\n",
+ dev, err < 0 ? strerror(-err):
+ nvme_status_to_string(err, false));
+ return 0;
+ }
+
+ strncpy(item->dev, dev, sizeof(item->dev) - 1);
+
+ return 1;
+}
+
+static int netapp_nvme_filter(const struct dirent *d)
+{
+ char path[264];
+ struct stat bd;
+ int ctrl, ns, partition;
+
+ if (d->d_name[0] == '.')
+ return 0;
+
+ if (strstr(d->d_name, "nvme")) {
+ snprintf(path, sizeof(path), "%s%s", dev_path, d->d_name);
+ if (stat(path, &bd))
+ return 0;
+ if (sscanf(d->d_name, "nvme%dn%d", &ctrl, &ns) != 2)
+ return 0;
+ if (sscanf(d->d_name, "nvme%dn%dp%d", &ctrl, &ns, &partition) == 3)
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+static int netapp_output_format(char *format)
+{
+ if (!format)
+ return -EINVAL;
+ if (!strcmp(format, "normal"))
+ return NNORMAL;
+ if (!strcmp(format, "json"))
+ return NJSON;
+ if (!strcmp(format, "column"))
+ return NCOLUMN;
+ return -EINVAL;
+}
+
+/* handler for 'nvme netapp smdevices' */
+static int netapp_smdevices(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Display information about E-Series volumes.";
+
+ struct dirent **devices;
+ int num, i, fd, ret, fmt;
+ struct smdevice_info *smdevices;
+ char path[264];
+ int num_smdevices = 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|column"),
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ fmt = netapp_output_format(cfg.output_format);
+ if (fmt != NNORMAL && fmt != NCOLUMN && fmt != NJSON) {
+ fprintf(stderr, "Unrecognized output format: %s\n", cfg.output_format);
+ return -EINVAL;
+ }
+
+ num = scandir(dev_path, &devices, netapp_nvme_filter, alphasort);
+ if (num <= 0) {
+ fprintf(stderr, "No NVMe devices detected.\n");
+ return num;
+ }
+
+ smdevices = calloc(num, sizeof(*smdevices));
+ if (!smdevices) {
+ fprintf(stderr, "Unable to allocate memory for devices.\n");
+ return ENOMEM;
+ }
+
+ for (i = 0; i < num; i++) {
+ snprintf(path, sizeof(path), "%s%s", dev_path,
+ devices[i]->d_name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s: %s\n", path,
+ strerror(errno));
+ continue;
+ }
+
+ num_smdevices += netapp_smdevices_get_info(fd,
+ &smdevices[num_smdevices], path);
+ close(fd);
+ }
+
+ if (num_smdevices)
+ netapp_smdevices_print(smdevices, num_smdevices, fmt);
+
+ for (i = 0; i < num; i++)
+ free(devices[i]);
+ free(devices);
+ free(smdevices);
+ return 0;
+}
+
+/* handler for 'nvme netapp ontapdevices' */
+static int netapp_ontapdevices(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Display information about ONTAP devices.";
+ struct dirent **devices;
+ int num, i, fd, ret, fmt;
+ struct ontapdevice_info *ontapdevices;
+ char path[264];
+ int num_ontapdevices = 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|column"),
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ fmt = netapp_output_format(cfg.output_format);
+ if (fmt != NNORMAL && fmt != NCOLUMN && fmt != NJSON) {
+ fprintf(stderr, "Unrecognized output format: %s\n", cfg.output_format);
+ return -EINVAL;
+ }
+
+ num = scandir(dev_path, &devices, netapp_nvme_filter, alphasort);
+ if (num <= 0) {
+ fprintf(stderr, "No NVMe devices detected.\n");
+ return num;
+ }
+
+ ontapdevices = calloc(num, sizeof(*ontapdevices));
+ if (!ontapdevices) {
+ fprintf(stderr, "Unable to allocate memory for devices.\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < num; i++) {
+ snprintf(path, sizeof(path), "%s%s", dev_path,
+ devices[i]->d_name);
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to open %s: %s\n", path,
+ strerror(errno));
+ continue;
+ }
+
+ num_ontapdevices += netapp_ontapdevices_get_info(fd,
+ &ontapdevices[num_ontapdevices], path);
+
+ close(fd);
+ }
+
+ if (num_ontapdevices)
+ netapp_ontapdevices_print(ontapdevices, num_ontapdevices, fmt);
+
+ for (i = 0; i < num; i++)
+ free(devices[i]);
+ free(devices);
+ free(ontapdevices);
+ return 0;
+}
diff --git a/plugins/netapp/netapp-nvme.h b/plugins/netapp/netapp-nvme.h
new file mode 100644
index 0000000..73de4b4
--- /dev/null
+++ b/plugins/netapp/netapp-nvme.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/netapp/netapp-nvme
+
+#if !defined(NETAPP_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define NETAPP_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("netapp", "NetApp vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("smdevices", "NetApp SMdevices", netapp_smdevices)
+ ENTRY("ontapdevices", "NetApp ONTAPdevices", netapp_ontapdevices)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/nvidia/nvidia-nvme.c b/plugins/nvidia/nvidia-nvme.c
new file mode 100644
index 0000000..71e0bc3
--- /dev/null
+++ b/plugins/nvidia/nvidia-nvme.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.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..3d870e1
--- /dev/null
+++ b/plugins/nvidia/nvidia-nvme.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#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", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("id-ctrl", "Send NVMe Identify Controller", id_ctrl)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/ocp/meson.build b/plugins/ocp/meson.build
new file mode 100644
index 0000000..a4e5d20
--- /dev/null
+++ b/plugins/ocp/meson.build
@@ -0,0 +1,6 @@
+sources += [
+ 'plugins/ocp/ocp-utils.c',
+ 'plugins/ocp/ocp-nvme.c',
+ 'plugins/ocp/ocp-clear-fw-update-history.c',
+]
+
diff --git a/plugins/ocp/ocp-clear-fw-update-history.c b/plugins/ocp/ocp-clear-fw-update-history.c
new file mode 100644
index 0000000..fef09cf
--- /dev/null
+++ b/plugins/ocp/ocp-clear-fw-update-history.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Authors: haro.panosyan@solidigm.com
+ * leonardo.da.cunha@solidigm.com
+ */
+
+#include <unistd.h>
+#include "ocp-utils.h"
+#include "nvme-print.h"
+
+static const __u8 OCP_FID_CLEAR_FW_ACTIVATION_HISTORY = 0xC1;
+
+int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "OCP Clear Firmware Update History";
+ __u32 result = 0;
+ __u32 clear_fw_history = 1 << 31;
+ struct nvme_dev *dev;
+ int uuid_index = 0;
+ bool no_uuid = false;
+ int err;
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("no-uuid", 'n', &no_uuid,
+ "Skip UUID index search (UUID index not required for OCP 1.0)"),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+ if (no_uuid == false) {
+ // OCP 2.0 requires UUID index support
+ err = ocp_get_uuid_index(dev, &uuid_index);
+ if (err || uuid_index == 0) {
+ fprintf(stderr, "ERROR: No OCP UUID index found\n");
+ goto close_dev;
+ }
+ }
+
+ struct nvme_set_features_args args = {
+ .result = &result,
+ .data = NULL,
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .nsid = 0,
+ .cdw11 = clear_fw_history,
+ .cdw12 = 0,
+ .cdw13 = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .save = 0,
+ .uuidx = uuid_index,
+ .fid = OCP_FID_CLEAR_FW_ACTIVATION_HISTORY,
+ };
+
+ err = nvme_set_features(&args);
+
+ if (err == 0)
+ printf("Success : %s\n", desc);
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ printf("Fail : %s\n", desc);
+close_dev:
+ /* Redundant close() to make static code analysis happy */
+ close(dev->direct.fd);
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/ocp/ocp-clear-fw-update-history.h b/plugins/ocp/ocp-clear-fw-update-history.h
new file mode 100644
index 0000000..25fb6b1
--- /dev/null
+++ b/plugins/ocp/ocp-clear-fw-update-history.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Authors: haro.panosyan@solidigm.com
+ * leonardo.da.cunha@solidigm.com
+ */
+
+int ocp_clear_fw_update_history(int argc, char **argv, struct command *cmd, struct plugin *plugin);
diff --git a/plugins/ocp/ocp-nvme.c b/plugins/ocp/ocp-nvme.c
new file mode 100644
index 0000000..14a5f30
--- /dev/null
+++ b/plugins/ocp/ocp-nvme.c
@@ -0,0 +1,774 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2022 Meta Platforms, Inc.
+ *
+ * Authors: Arthur Shau <arthurshau@fb.com>,
+ * Wei Zhang <wzhang@fb.com>,
+ * Venkat Ramesh <venkatraghavan@fb.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "util/types.h"
+#include "nvme-print.h"
+#include "ocp-clear-fw-update-history.h"
+
+#define CREATE_CMD
+#include "ocp-nvme.h"
+
+/* C0 SCAO Log Page */
+#define C0_SMART_CLOUD_ATTR_LEN 0x200
+#define C0_SMART_CLOUD_ATTR_OPCODE 0xC0
+#define C0_GUID_LENGTH 16
+#define C0_ACTIVE_BUCKET_TIMER_INCREMENT 5
+#define C0_ACTIVE_THRESHOLD_INCREMENT 5
+#define C0_MINIMUM_WINDOW_INCREMENT 100
+
+static __u8 scao_guid[C0_GUID_LENGTH] = { 0xC5, 0xAF, 0x10, 0x28, 0xEA, 0xBF,
+ 0xF2, 0xA4, 0x9C, 0x4F, 0x6F, 0x7C, 0xC9, 0x14, 0xD5, 0xAF };
+
+/* C3 Latency Monitor Log Page */
+#define C3_LATENCY_MON_LOG_BUF_LEN 0x200
+#define C3_LATENCY_MON_OPCODE 0xC3
+#define C3_LATENCY_MON_VERSION 0x0001
+#define C3_GUID_LENGTH 16
+static __u8 lat_mon_guid[C3_GUID_LENGTH] = { 0x92, 0x7a, 0xc0, 0x8c, 0xd0, 0x84,
+ 0x6c, 0x9c, 0x70, 0x43, 0xe6, 0xd4, 0x58, 0x5e, 0xd4, 0x85 };
+
+#define READ 0
+#define WRITE 1
+#define TRIM 2
+#define RESERVED 3
+
+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;
+
+struct __attribute__((__packed__)) ssd_latency_monitor_log {
+ __u8 feature_status; /* 0x00 */
+ __u8 rsvd1; /* 0x01 */
+ __le16 active_bucket_timer; /* 0x02 */
+ __le16 active_bucket_timer_threshold; /* 0x04 */
+ __u8 active_threshold_a; /* 0x06 */
+ __u8 active_threshold_b; /* 0x07 */
+ __u8 active_threshold_c; /* 0x08 */
+ __u8 active_threshold_d; /* 0x09 */
+ __le16 active_latency_config; /* 0x0A */
+ __u8 active_latency_min_window; /* 0x0C */
+ __u8 rsvd2[0x13]; /* 0x0D */
+
+ __le32 active_bucket_counter[4][4] ; /* 0x20 - 0x5F */
+ __le64 active_latency_timestamp[4][3]; /* 0x60 - 0xBF */
+ __le16 active_measured_latency[4][3]; /* 0xC0 - 0xD7 */
+ __le16 active_latency_stamp_units; /* 0xD8 */
+ __u8 rsvd3[0x16]; /* 0xDA */
+
+ __le32 static_bucket_counter[4][4] ; /* 0xF0 - 0x12F */
+ __le64 static_latency_timestamp[4][3]; /* 0x130 - 0x18F */
+ __le16 static_measured_latency[4][3]; /* 0x190 - 0x1A7 */
+ __le16 static_latency_stamp_units; /* 0x1A8 */
+ __u8 rsvd4[0x16]; /* 0x1AA */
+
+ __le16 debug_log_trigger_enable; /* 0x1C0 */
+ __le16 debug_log_measured_latency; /* 0x1C2 */
+ __le64 debug_log_latency_stamp; /* 0x1C4 */
+ __le16 debug_log_ptr; /* 0x1CC */
+ __le16 debug_log_counter_trigger; /* 0x1CE */
+ __u8 debug_log_stamp_units; /* 0x1D0 */
+ __u8 rsvd5[0x1D]; /* 0x1D1 */
+
+ __le16 log_page_version; /* 0x1EE */
+ __u8 log_page_guid[0x10]; /* 0x1F0 */
+};
+
+static int convert_ts(time_t time, char *ts_buf)
+{
+ struct tm gmTimeInfo;
+ time_t time_Human, time_ms;
+ char buf[80];
+
+ time_Human = time/1000;
+ time_ms = time % 1000;
+
+ gmtime_r((const time_t *)&time_Human, &gmTimeInfo);
+
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &gmTimeInfo);
+ sprintf(ts_buf, "%s.%03ld GMT", buf, time_ms);
+
+ return 0;
+}
+
+static void ocp_print_C0_log_normal(void *data)
+{
+ __u8 *log_data = (__u8*)data;
+ uint16_t smart_log_ver = 0;
+
+ printf("SMART Cloud Attributes :- \n");
+
+ printf(" Physical media units written - %"PRIu64" %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
+ printf(" Physical media units read - %"PRIu64" %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
+ 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 %s\n",
+ uint128_t_to_string(le128_to_cpu(&log_data[SCAO_PSC])));
+ printf(" Endurance estimate %s\n",
+ uint128_t_to_string(le128_to_cpu(&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("%"PRIx64"%"PRIx64"\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 ocp_print_C0_log_json(void *data)
+{
+ __u8 *log_data = (__u8*)data;
+ struct json_object *root;
+ struct json_object *pmuw;
+ struct json_object *pmur;
+ uint16_t smart_log_ver = 0;
+
+ root = json_create_object();
+ pmuw = json_create_object();
+ pmur = json_create_object();
+
+ json_object_add_value_uint64(pmuw, "hi",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW+8] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_uint64(pmuw, "lo",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUW] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_object(root, "Physical media units written", pmuw);
+ json_object_add_value_uint64(pmur, "hi",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR+8] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_uint64(pmur, "lo",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PMUR] & 0xFFFFFFFFFFFFFFFF));
+ json_object_add_value_object(root, "Physical media units read", pmur);
+ json_object_add_value_uint64(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_uint64(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_uint64(root, "XOR recovery count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC]));
+ json_object_add_value_uint64(root, "Uncorrectable read error count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC]));
+ json_object_add_value_uint64(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_uint64(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_uint64(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_uint64(root, "Unaligned I/O",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO]));
+ json_object_add_value_uint64(root, "Security Version Number",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN]));
+ json_object_add_value_uint64(root, "NUSE - Namespace utilization",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE]));
+ json_object_add_value_uint128(root, "PLP start count",
+ le128_to_cpu(&log_data[SCAO_PSC]));
+ json_object_add_value_uint128(root, "Endurance estimate",
+ le128_to_cpu(&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%"PRIx64"%"PRIx64"",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG]));
+ 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 int get_c0_log_page(int fd, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ int i;
+
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return fmt;
+ }
+
+ if ((data = (__u8 *) malloc(sizeof(__u8) * C0_SMART_CLOUD_ATTR_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * C0_SMART_CLOUD_ATTR_LEN);
+
+ ret = nvme_get_log_simple(fd, C0_SMART_CLOUD_ATTR_OPCODE,
+ C0_SMART_CLOUD_ATTR_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(ret, false), ret);
+
+ if (ret == 0) {
+
+ /* check log page guid */
+ /* Verify GUID matches */
+ for (i=0; i<16; i++) {
+ if (scao_guid[i] != data[SCAO_LPG + i]) {
+ fprintf(stderr, "ERROR : OCP : Unknown GUID in C0 Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", scao_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", data[SCAO_LPG + j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* print the data */
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_C0_log_normal(data);
+ break;
+ case JSON:
+ ocp_print_C0_log_json(data);
+ break;
+ }
+ } else {
+ fprintf(stderr, "ERROR : OCP : Unable to read C0 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int ocp_smart_add_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve latency monitor log data.";
+ struct nvme_dev *dev;
+ int ret = 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c0_log_page(dev_fd(dev), cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : OCP : Failure reading the C0 Log Page, ret = %d\n",
+ ret);
+ dev_close(dev);
+ return ret;
+}
+
+static int ocp_print_C3_log_normal(struct nvme_dev *dev,
+ struct ssd_latency_monitor_log *log_data)
+{
+ printf("-Latency Monitor/C3 Log Page Data- \n");
+ printf(" Controller : %s\n", dev->name);
+ int i, j;
+ int pos = 0;
+ char ts_buf[128];
+
+ printf(" Feature Status 0x%x \n",
+ log_data->feature_status);
+ printf(" Active Bucket Timer %d min \n",
+ C0_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer));
+ printf(" Active Bucket Timer Threshold %d min \n",
+ C0_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer_threshold));
+ printf(" Active Threshold A %d ms \n",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_a+1));
+ printf(" Active Threshold B %d ms \n",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_b+1));
+ printf(" Active Threshold C %d ms \n",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_c+1));
+ printf(" Active Threshold D %d ms \n",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_d+1));
+ printf(" Active Latency Minimum Window %d ms \n",
+ C0_MINIMUM_WINDOW_INCREMENT *
+ le16_to_cpu(log_data->active_latency_min_window));
+ printf(" Active Latency Stamp Units %d \n",
+ le16_to_cpu(log_data->active_latency_stamp_units));
+ printf(" Static Latency Stamp Units %d \n",
+ le16_to_cpu(log_data->static_latency_stamp_units));
+ printf(" Debug Log Trigger Enable %d \n",
+ le16_to_cpu(log_data->debug_log_trigger_enable));
+
+ printf(" Read Write Deallocate/Trim \n");
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Latency Mode: Bucket %d %27d %27d %27d \n",
+ i,
+ log_data->active_latency_config & (1 << pos),
+ log_data->active_latency_config & (1 << pos),
+ log_data->active_latency_config & (1 << pos));
+ }
+ printf("\n");
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Bucket Counter: Bucket %d %27d %27d %27d \n",
+ i,
+ le32_to_cpu(log_data->active_bucket_counter[i][READ]),
+ le32_to_cpu(log_data->active_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->active_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
+ i,
+ le16_to_cpu(log_data->active_measured_latency[i][READ]),
+ le16_to_cpu(log_data->active_measured_latency[i][WRITE]),
+ le16_to_cpu(log_data->active_measured_latency[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Latency Time Stamp: Bucket %d ", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1)
+ printf(" N/A ");
+ else {
+ convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Bucket Counter: Bucket %d %27d %27d %27d \n",
+ i,
+ le32_to_cpu(log_data->static_bucket_counter[i][READ]),
+ le32_to_cpu(log_data->static_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->static_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
+ i,
+ le16_to_cpu(log_data->static_measured_latency[i][READ]),
+ le16_to_cpu(log_data->static_measured_latency[i][WRITE]),
+ le16_to_cpu(log_data->static_measured_latency[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Latency Time Stamp: Bucket %d ", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1)
+ printf(" N/A ");
+ else {
+ convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static void ocp_print_C3_log_json(struct ssd_latency_monitor_log *log_data)
+{
+ int i, j;
+ int pos = 0;
+ char buf[128];
+ char ts_buf[128];
+ char *operation[3] = {"Read", "Write", "Trim"};
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_uint(root, "Feature Status",
+ log_data->feature_status);
+ json_object_add_value_uint(root, "Active Bucket Timer",
+ C0_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer));
+ json_object_add_value_uint(root, "Active Bucket Timer Threshold",
+ C0_ACTIVE_BUCKET_TIMER_INCREMENT *
+ le16_to_cpu(log_data->active_bucket_timer_threshold));
+ json_object_add_value_uint(root, "Active Threshold A",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_a+1));
+ json_object_add_value_uint(root, "Active Threshold B",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_b+1));
+ json_object_add_value_uint(root, "Active Threshold C",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_c+1));
+ json_object_add_value_uint(root, "Active Threshold D",
+ C0_ACTIVE_THRESHOLD_INCREMENT *
+ le16_to_cpu(log_data->active_threshold_d+1));
+ json_object_add_value_uint(root, "Active Lantency Minimum Window",
+ C0_MINIMUM_WINDOW_INCREMENT *
+ le16_to_cpu(log_data->active_latency_min_window));
+ json_object_add_value_uint(root, "Active Latency Stamp Units",
+ le16_to_cpu(log_data->active_latency_stamp_units));
+ json_object_add_value_uint(root, "Static Latency Stamp Units",
+ le16_to_cpu(log_data->static_latency_stamp_units));
+ json_object_add_value_uint(root, "Debug Log Trigger Enable",
+ le16_to_cpu(log_data->debug_log_trigger_enable));
+
+ for (i = 0; i <= 3; i++) {
+ struct json_object *bucket;
+ bucket = json_create_object();
+ sprintf(buf, "Active Latency Mode: Bucket %d", i);
+ for (j = 0; j <= 2; j++) {
+ json_object_add_value_uint(bucket, operation[j],
+ log_data->active_latency_config & (1 << pos));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+ for (i = 0; i <= 3; i++) {
+ struct json_object *bucket;
+ bucket = json_create_object();
+ sprintf(buf, "Active Bucket Counter: Bucket %d", i);
+ for (j = 0; j <= 2; j++) {
+ json_object_add_value_uint(bucket, operation[j],
+ le32_to_cpu(log_data->active_bucket_counter[i][j]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+ for (i = 0; i <= 3; i++) {
+ struct json_object *bucket;
+ bucket = json_create_object();
+ sprintf(buf, "Active Measured Latency: Bucket %d", i);
+ for (j = 0; j <= 2; j++) {
+ json_object_add_value_uint(bucket, operation[j],
+ le16_to_cpu(log_data->active_measured_latency[i][j]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+ for (i = 0; i <= 3; i++) {
+ struct json_object *bucket;
+ bucket = json_create_object();
+ sprintf(buf, "Active Latency Time Stamp: Bucket %d", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1)
+ json_object_add_value_string(bucket, operation[j], "NA");
+ else {
+ convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf);
+ json_object_add_value_string(bucket, operation[j], ts_buf);
+ }
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+ for (i = 0; i <= 3; i++) {
+ struct json_object *bucket;
+ bucket = json_create_object();
+ sprintf(buf, "Static Bucket Counter: Bucket %d", i);
+ for (j = 0; j <= 2; j++) {
+ json_object_add_value_uint(bucket, operation[j],
+ le32_to_cpu(log_data->static_bucket_counter[i][j]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+ for (i = 0; i <= 3; i++) {
+ struct json_object *bucket;
+ bucket = json_create_object();
+ sprintf(buf, "Static Measured Latency: Bucket %d", i);
+ for (j = 0; j <= 2; j++) {
+ json_object_add_value_uint(bucket, operation[j],
+ le16_to_cpu(log_data->static_measured_latency[i][j]));
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+ for (i = 0; i <= 3; i++) {
+ struct json_object *bucket;
+ bucket = json_create_object();
+ sprintf(buf, "Static Latency Time Stamp: Bucket %d", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1)
+ json_object_add_value_string(bucket, operation[j], "NA");
+ else {
+ convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf);
+ json_object_add_value_string(bucket, operation[j], ts_buf);
+ }
+ }
+ json_object_add_value_object(root, buf, bucket);
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static int get_c3_log_page(struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ int i;
+ struct ssd_latency_monitor_log *log_data;
+
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : OCP : invalid output format\n");
+ return fmt;
+ }
+
+ if ((data = (__u8 *) malloc(sizeof(__u8) * C3_LATENCY_MON_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : OCP : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * C3_LATENCY_MON_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), C3_LATENCY_MON_OPCODE,
+ C3_LATENCY_MON_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr,
+ "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(ret, false),
+ ret);
+
+ if (ret == 0) {
+ log_data = (struct ssd_latency_monitor_log*)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != C3_LATENCY_MON_VERSION) {
+ fprintf(stderr,
+ "ERROR : OCP : invalid latency monitor version\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* check log page guid */
+ /* Verify GUID matches */
+ for (i=0; i<16; i++) {
+ if (lat_mon_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr,"ERROR : OCP : Unknown GUID in C3 Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : OCP : Expected GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", lat_mon_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : OCP : Actual GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ switch (fmt) {
+ case NORMAL:
+ ocp_print_C3_log_normal(dev, log_data);
+ break;
+ case JSON:
+ ocp_print_C3_log_json(log_data);
+ break;
+ }
+ } else {
+ fprintf(stderr,
+ "ERROR : OCP : Unable to read C3 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int ocp_latency_monitor_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve latency monitor log data.";
+ struct nvme_dev *dev;
+ int ret = 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = get_c3_log_page(dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr,
+ "ERROR : OCP : Failure reading the C3 Log Page, ret = %d\n",
+ ret);
+ dev_close(dev);
+ return ret;
+}
+
+static int clear_fw_update_history(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ return ocp_clear_fw_update_history(argc, argv, cmd, plugin);
+}
diff --git a/plugins/ocp/ocp-nvme.h b/plugins/ocp/ocp-nvme.h
new file mode 100644
index 0000000..c20646a
--- /dev/null
+++ b/plugins/ocp/ocp-nvme.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (c) 2022 Meta Platforms, Inc.
+ *
+ * Authors: Arthur Shau <arthurshau@fb.com>,
+ * Wei Zhang <wzhang@fb.com>,
+ * Venkat Ramesh <venkatraghavan@fb.com>
+ */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/ocp/ocp-nvme
+
+#if !defined(OCP_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define OCP_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("ocp", "OCP cloud SSD extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("smart-add-log", "Retrieve extended SMART Information", ocp_smart_add_log)
+ ENTRY("latency-monitor-log", "Get Latency Monitor Log Page",
+ ocp_latency_monitor_log)
+ ENTRY("clear-fw-activate-history", "Clear firmware update history log",
+ clear_fw_update_history)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/ocp/ocp-utils.c b/plugins/ocp/ocp-utils.c
new file mode 100644
index 0000000..9294c05
--- /dev/null
+++ b/plugins/ocp/ocp-utils.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "ocp-utils.h"
+
+const unsigned char ocp_uuid[NVME_UUID_LEN] = {
+ 0x6f, 0xbe, 0x56, 0x8f, 0x99, 0x29, 0x1d, 0xa2, 0x94, 0x47,
+ 0x94, 0xe0, 0x5b, 0xd5, 0x94, 0xc1 };
+
+int ocp_get_uuid_index(struct nvme_dev *dev, int *index)
+{
+ struct nvme_id_uuid_list uuid_list;
+ int err = nvme_identify_uuid(dev_fd(dev), &uuid_list);
+
+ *index = 0;
+ if (err)
+ return err;
+
+ for (int i = 0; i < NVME_ID_UUID_LIST_MAX; i++) {
+ if (memcmp(ocp_uuid, &uuid_list.entry[i].uuid, NVME_UUID_LEN) == 0) {
+ *index = i + 1;
+ break;
+ }
+ }
+ return err;
+}
diff --git a/plugins/ocp/ocp-utils.h b/plugins/ocp/ocp-utils.h
new file mode 100644
index 0000000..44d0af4
--- /dev/null
+++ b/plugins/ocp/ocp-utils.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "nvme.h"
+
+/**
+ * ocp_get_uuid_index() - Get OCP UUID index
+ * @dev: nvme device
+ * @index: integer ponter to here to save the index
+ * @result: The command completion result from CQE dword0
+ *
+ * Return: Zero if nvme device has UUID list log page, or result of get uuid list otherwise.
+ */
+int ocp_get_uuid_index(struct nvme_dev *dev, int *index);
diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c
new file mode 100644
index 0000000..4bcfbf6
--- /dev/null
+++ b/plugins/scaleflux/sfx-nvme.c
@@ -0,0 +1,1213 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <linux/fs.h>
+#include <inttypes.h>
+#include <asm/byteorder.h>
+#include <sys/sysinfo.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "sfx-nvme.h"
+
+#define SFX_PAGE_SHIFT 12
+#define SECTOR_SHIFT 9
+
+#define SFX_GET_FREESPACE _IOWR('N', 0x240, struct sfx_freespace_ctx)
+#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)
+
+#define VANDA_MAJOR_IDX 0
+#define VANDA_MINOR_IDX 0
+
+#define MYRTLE_MAJOR_IDX 4
+#define MYRTLE_MINOR_IDX 1
+
+enum {
+ SFX_LOG_LATENCY_READ_STATS = 0xc1,
+ SFX_LOG_SMART = 0xc2,
+ 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,
+};
+
+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 out_of_space;
+};
+
+struct nvme_capacity_info {
+ __u64 lba_sec_sz;
+ __u64 phy_sec_sz;
+ __u64 used_space;
+ __u64 free_space;
+};
+
+struct __attribute__((packed)) nvme_additional_smart_log_item {
+ __u8 key;
+ __u8 _kp[2];
+ __u8 norm;
+ __u8 _np;
+ union __attribute__((packed)) {
+ __u8 raw[6];
+ struct __attribute__((packed)) wear_level {
+ __le16 min;
+ __le16 max;
+ __le16 avg;
+ } wear_level;
+ struct __attribute__((packed)) thermal_throttle {
+ __u8 pct;
+ __u32 count;
+ } thermal_throttle;
+ } ;
+ __u8 _rp;
+} ;
+
+struct nvme_additional_smart_log {
+ struct nvme_additional_smart_log_item program_fail_cnt;
+ struct nvme_additional_smart_log_item erase_fail_cnt;
+ struct nvme_additional_smart_log_item wear_leveling_cnt;
+ struct nvme_additional_smart_log_item e2e_err_cnt;
+ struct nvme_additional_smart_log_item crc_err_cnt;
+ struct nvme_additional_smart_log_item timed_workload_media_wear;
+ struct nvme_additional_smart_log_item timed_workload_host_reads;
+ struct nvme_additional_smart_log_item timed_workload_timer;
+ struct nvme_additional_smart_log_item thermal_throttle_status;
+ struct nvme_additional_smart_log_item retry_buffer_overflow_cnt;
+ 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 raid_recover_cnt; // errors which can be recovered by RAID
+ struct nvme_additional_smart_log_item prog_timeout_cnt;
+ struct nvme_additional_smart_log_item erase_timeout_cnt;
+ struct nvme_additional_smart_log_item read_timeout_cnt;
+ struct nvme_additional_smart_log_item read_ecc_cnt;//retry cnt
+ struct nvme_additional_smart_log_item non_media_crc_err_cnt;
+ struct nvme_additional_smart_log_item compression_path_err_cnt;
+ struct nvme_additional_smart_log_item out_of_space_flag;
+ struct nvme_additional_smart_log_item physical_usage_ratio;
+ struct nvme_additional_smart_log_item grown_bb; //grown bad block
+};
+
+int nvme_query_cap(int fd, __u32 nsid, __u32 data_len, void *data)
+{
+ int rc = 0;
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_query_cap_info,
+ .nsid = nsid,
+ .addr = (__u64)(uintptr_t) data,
+ .data_len = data_len,
+ };
+
+ rc = ioctl(fd, SFX_GET_FREESPACE, data);
+ return rc == 0 ? 0 : nvme_submit_admin_passthru(fd, &cmd, NULL);
+}
+int nvme_change_cap(int fd, __u32 nsid, __u64 capacity)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_change_cap,
+ .nsid = nsid,
+ .cdw10 = (capacity & 0xffffffff),
+ .cdw11 = (capacity >> 32),
+ };
+
+ return nvme_submit_admin_passthru(fd, &cmd, NULL);
+}
+
+int nvme_sfx_set_features(int fd, __u32 nsid, __u32 fid, __u32 value)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_sfx_set_features,
+ .nsid = nsid,
+ .cdw10 = fid,
+ .cdw11 = value,
+ };
+
+ return nvme_submit_admin_passthru(fd, &cmd, NULL);
+}
+
+int nvme_sfx_get_features(int fd, __u32 nsid, __u32 fid, __u32 *result)
+{
+ int err = 0;
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_sfx_get_features,
+ .nsid = nsid,
+ .cdw10 = fid,
+ };
+
+ err = nvme_submit_admin_passthru(fd, &cmd, NULL);
+ if (!err && result) {
+ *result = cmd.result;
+ }
+
+ return err;
+}
+
+static void show_sfx_smart_log_jsn(struct nvme_additional_smart_log *smart,
+ unsigned int nsid, const char *devname)
+{
+ struct json_object *root, *entry_stats, *dev_stats, *multi;
+
+ root = json_create_object();
+ json_object_add_value_string(root, "Intel Smart log", devname);
+
+ dev_stats = json_create_object();
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->program_fail_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->program_fail_cnt.raw));
+ json_object_add_value_object(dev_stats, "program_fail_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->erase_fail_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->erase_fail_cnt.raw));
+ json_object_add_value_object(dev_stats, "erase_fail_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->wear_leveling_cnt.norm);
+ multi = json_create_object();
+ json_object_add_value_int(multi, "min", le16_to_cpu(smart->wear_leveling_cnt.wear_level.min));
+ json_object_add_value_int(multi, "max", le16_to_cpu(smart->wear_leveling_cnt.wear_level.max));
+ json_object_add_value_int(multi, "avg", le16_to_cpu(smart->wear_leveling_cnt.wear_level.avg));
+ json_object_add_value_object(entry_stats, "raw", multi);
+ json_object_add_value_object(dev_stats, "wear_leveling", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->e2e_err_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->e2e_err_cnt.raw));
+ json_object_add_value_object(dev_stats, "end_to_end_error_detection_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->crc_err_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->crc_err_cnt.raw));
+ json_object_add_value_object(dev_stats, "crc_error_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_media_wear.norm);
+ json_object_add_value_float(entry_stats, "raw", ((float)int48_to_long(smart->timed_workload_media_wear.raw)) / 1024);
+ json_object_add_value_object(dev_stats, "timed_workload_media_wear", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_host_reads.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_host_reads.raw));
+ json_object_add_value_object(dev_stats, "timed_workload_host_reads", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->timed_workload_timer.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->timed_workload_timer.raw));
+ json_object_add_value_object(dev_stats, "timed_workload_timer", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->thermal_throttle_status.norm);
+ multi = json_create_object();
+ json_object_add_value_int(multi, "pct", smart->thermal_throttle_status.thermal_throttle.pct);
+ json_object_add_value_int(multi, "cnt", smart->thermal_throttle_status.thermal_throttle.count);
+ json_object_add_value_object(entry_stats, "raw", multi);
+ json_object_add_value_object(dev_stats, "thermal_throttle_status", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->retry_buffer_overflow_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->retry_buffer_overflow_cnt.raw));
+ json_object_add_value_object(dev_stats, "retry_buffer_overflow_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->pll_lock_loss_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->pll_lock_loss_cnt.raw));
+ json_object_add_value_object(dev_stats, "pll_lock_loss_count", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->nand_bytes_written.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->nand_bytes_written.raw));
+ json_object_add_value_object(dev_stats, "nand_bytes_written", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->host_bytes_written.norm);
+ 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->raid_recover_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->raid_recover_cnt.raw));
+ json_object_add_value_object(dev_stats, "raid_recover_cnt", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->prog_timeout_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->prog_timeout_cnt.raw));
+ json_object_add_value_object(dev_stats, "prog_timeout_cnt", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->erase_timeout_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->erase_timeout_cnt.raw));
+ json_object_add_value_object(dev_stats, "erase_timeout_cnt", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->read_timeout_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->read_timeout_cnt.raw));
+ json_object_add_value_object(dev_stats, "read_timeout_cnt", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->read_ecc_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->read_ecc_cnt.raw));
+ json_object_add_value_object(dev_stats, "read_ecc_cnt", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->non_media_crc_err_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->non_media_crc_err_cnt.raw));
+ json_object_add_value_object(dev_stats, "non_media_crc_err_cnt", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->compression_path_err_cnt.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->compression_path_err_cnt.raw));
+ json_object_add_value_object(dev_stats, "compression_path_err_cnt", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->out_of_space_flag.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->out_of_space_flag.raw));
+ json_object_add_value_object(dev_stats, "out_of_space_flag", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->physical_usage_ratio.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->physical_usage_ratio.raw));
+ json_object_add_value_object(dev_stats, "physical_usage_ratio", entry_stats);
+
+ entry_stats = json_create_object();
+ json_object_add_value_int(entry_stats, "normalized", smart->grown_bb.norm);
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(smart->grown_bb.raw));
+ json_object_add_value_object(dev_stats, "grown_bb", entry_stats);
+
+ json_object_add_value_object(root, "Device stats", dev_stats);
+
+ json_print_object(root, NULL);
+ printf("/n");
+ json_free_object(root);
+}
+
+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));
+ printf("non_media_crc_err_cnt : %3d%% %" PRIu64 "\n",
+ smart->non_media_crc_err_cnt.norm,
+ int48_to_long(smart->non_media_crc_err_cnt.raw));
+ printf("compression_path_err_cnt : %3d%% %" PRIu64 "\n",
+ smart->compression_path_err_cnt.norm,
+ int48_to_long(smart->compression_path_err_cnt.raw));
+ printf("out_of_space_flag : %3d%% %" PRIu64 "\n",
+ smart->out_of_space_flag.norm,
+ int48_to_long(smart->out_of_space_flag.raw));
+ printf("phy_capacity_used_ratio : %3d%% %" PRIu64 "\n",
+ smart->physical_usage_ratio.norm,
+ int48_to_long(smart->physical_usage_ratio.raw));
+ printf("grown_bb_count : %3d%% %" PRIu64 "\n",
+ smart->grown_bb.norm, int48_to_long(smart->grown_bb.raw));
+
+
+}
+
+static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_additional_smart_log smart_log;
+ char *desc = "Get ScaleFlux 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";
+ const char *json= "Dump output in json format";
+ struct nvme_dev *dev;
+ struct config {
+ __u32 namespace_id;
+ bool raw_binary;
+ bool json;
+ };
+ int err;
+
+ struct config cfg = {
+ .namespace_id = 0xffffffff,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_FLAG("json", 'j', &cfg.json, json),
+ OPT_END()
+ };
+
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_nsid_log(dev_fd(dev), false, 0xca, cfg.namespace_id,
+ sizeof(smart_log), (void *)&smart_log);
+ if (!err) {
+ if (cfg.json)
+ show_sfx_smart_log_jsn(&smart_log, cfg.namespace_id,
+ dev->name);
+ else if (!cfg.raw_binary)
+ show_sfx_smart_log(&smart_log, cfg.namespace_id,
+ dev->name);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ else if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+struct __attribute__((__packed__)) sfx_lat_stats_vanda {
+ __u16 maj;
+ __u16 min;
+ __u32 bucket_1[32]; /* 0~1ms, step 32us */
+ __u32 bucket_2[31]; /* 1~32ms, step 1ms */
+ __u32 bucket_3[31]; /* 32ms~1s, step 32ms */
+ __u32 bucket_4[1]; /* 1s~2s, specifically 1024ms~2047ms */
+ __u32 bucket_5[1]; /* 2s~4s, specifically 2048ms~4095ms */
+ __u32 bucket_6[1]; /* 4s+, specifically 4096ms+ */
+};
+
+struct __attribute__((__packed__)) sfx_lat_stats_myrtle {
+ __u16 maj;
+ __u16 min;
+ __u32 bucket_1[64]; /* 0us~63us, step 1us */
+ __u32 bucket_2[64]; /* 63us~127us, step 1us */
+ __u32 bucket_3[64]; /* 127us~255us, step 2us */
+ __u32 bucket_4[64]; /* 255us~510us, step 4us */
+ __u32 bucket_5[64]; /* 510us~1.02ms step 8us */
+ __u32 bucket_6[64]; /* 1.02ms~2.04ms step 16us */
+ __u32 bucket_7[64]; /* 2.04ms~4.08ms step 32us */
+ __u32 bucket_8[64]; /* 4.08ms~8.16ms step 64us */
+ __u32 bucket_9[64]; /* 8.16ms~16.32ms step 128us */
+ __u32 bucket_10[64]; /* 16.32ms~32.64ms step 256us */
+ __u32 bucket_11[64]; /* 32.64ms~65.28ms step 512us */
+ __u32 bucket_12[64]; /* 65.28ms~130.56ms step 1.024ms */
+ __u32 bucket_13[64]; /* 130.56ms~261.12ms step 2.048ms */
+ __u32 bucket_14[64]; /* 261.12ms~522.24ms step 4.096ms */
+ __u32 bucket_15[64]; /* 522.24ms~1.04s step 8.192ms */
+ __u32 bucket_16[64]; /* 1.04s~2.09s step 16.384ms */
+ __u32 bucket_17[64]; /* 2.09s~4.18s step 32.768ms */
+ __u32 bucket_18[64]; /* 4.18s~8.36s step 65.536ms */
+ __u32 bucket_19[64]; /* 8.36s~ step 131.072ms */
+ __u64 average; /* average latency statistics */
+};
+
+
+struct __attribute__((__packed__)) sfx_lat_status_ver {
+ __u16 maj;
+ __u16 min;
+};
+
+struct sfx_lat_stats {
+ union {
+ struct sfx_lat_status_ver ver;
+ struct sfx_lat_stats_vanda vanda;
+ struct sfx_lat_stats_myrtle myrtle;
+ };
+};
+
+static void show_lat_stats_vanda(struct sfx_lat_stats_vanda *stats, int write)
+{
+ int i;
+
+ printf("ScaleFlux IO %s Command Latency Statistics\n", write ? "Write" : "Read");
+ printf("-------------------------------------\n");
+ printf("Major Revision : %u\n", stats->maj);
+ printf("Minor Revision : %u\n", stats->min);
+
+ printf("\nGroup 1: Range is 0-1ms, step is 32us\n");
+ for (i = 0; i < 32; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_1[i]);
+
+ printf("\nGroup 2: Range is 1-32ms, step is 1ms\n");
+ for (i = 0; i < 31; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_2[i]);
+
+ printf("\nGroup 3: Range is 32ms-1s, step is 32ms:\n");
+ for (i = 0; i < 31; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_3[i]);
+
+ printf("\nGroup 4: Range is 1s-2s:\n");
+ printf("Bucket %2d: %u\n", 0, stats->bucket_4[0]);
+
+ printf("\nGroup 5: Range is 2s-4s:\n");
+ printf("Bucket %2d: %u\n", 0, stats->bucket_5[0]);
+
+ printf("\nGroup 6: Range is 4s+:\n");
+ printf("Bucket %2d: %u\n", 0, stats->bucket_6[0]);
+}
+
+static void show_lat_stats_myrtle(struct sfx_lat_stats_myrtle *stats, int write)
+{
+ int i;
+
+ printf("ScaleFlux IO %s Command Latency Statistics\n", write ? "Write" : "Read");
+ printf("-------------------------------------\n");
+ printf("Major Revision : %u\n", stats->maj);
+ printf("Minor Revision : %u\n", stats->min);
+
+ printf("\nGroup 1: Range is 0us~63us, step 1us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_1[i]);
+
+ printf("\nGroup 2: Range is 63us~127us, step 1us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_2[i]);
+
+ printf("\nGroup 3: Range is 127us~255us, step 2us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_3[i]);
+
+ printf("\nGroup 4: Range is 255us~510us, step 4us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_4[i]);
+
+ printf("\nGroup 5: Range is 510us~1.02ms step\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_5[i]);
+
+ printf("\nGroup 6: Range is 1.02ms~2.04ms step 16us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_6[i]);
+
+ printf("\nGroup 7: Range is 2.04ms~4.08ms step 32us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_7[i]);
+
+ printf("\nGroup 8: Range is 4.08ms~8.16ms step 64us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_8[i]);
+
+ printf("\nGroup 9: Range is 8.16ms~16.32ms step 128us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_9[i]);
+
+ printf("\nGroup 10: Range is 16.32ms~32.64ms step 256us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_10[i]);
+
+ printf("\nGroup 11: Range is 32.64ms~65.28ms step 512us\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_11[i]);
+
+ printf("\nGroup 12: Range is 65.28ms~130.56ms step 1.024ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_12[i]);
+
+ printf("\nGroup 13: Range is 130.56ms~261.12ms step 2.048ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_13[i]);
+
+ printf("\nGroup 14: Range is 261.12ms~522.24ms step 4.096ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_14[i]);
+
+ printf("\nGroup 15: Range is 522.24ms~1.04s step 8.192ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_15[i]);
+
+ printf("\nGroup 16: Range is 1.04s~2.09s step 16.384ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_16[i]);
+
+ printf("\nGroup 17: Range is 2.09s~4.18s step 32.768ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_17[i]);
+
+ printf("\nGroup 18: Range is 4.18s~8.36s step 65.536ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_18[i]);
+
+ printf("\nGroup 19: Range is 8.36s~ step 131.072ms\n");
+ for (i = 0; i < 64; i++)
+ printf("Bucket %2d: %u\n", i, stats->bucket_19[i]);
+
+ printf("\nAverage latency statistics %" PRIu64 "\n",
+ (uint64_t)stats->average);
+}
+
+
+static int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct sfx_lat_stats stats;
+ char *desc = "Get ScaleFlux Latency Statistics log and show it.";
+ const char *raw = "dump output in binary format";
+ const char *write = "Get write statistics (read default)";
+ struct nvme_dev *dev;
+ struct config {
+ bool raw_binary;
+ bool write;
+ };
+ int err;
+
+ struct config cfg = {
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("write", 'w', &cfg.write, write),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_log_simple(dev_fd(dev), cfg.write ? 0xc3 : 0xc1,
+ sizeof(stats), (void *)&stats);
+ if (!err) {
+ if ((stats.ver.maj == VANDA_MAJOR_IDX) && (stats.ver.min == VANDA_MINOR_IDX)) {
+ if (!cfg.raw_binary) {
+ show_lat_stats_vanda(&stats.vanda, cfg.write);
+ } else {
+ d_raw((unsigned char *)&stats.vanda, sizeof(struct sfx_lat_stats_vanda));
+ }
+ } else if ((stats.ver.maj == MYRTLE_MAJOR_IDX) && (stats.ver.min == MYRTLE_MINOR_IDX)) {
+ if (!cfg.raw_binary) {
+ show_lat_stats_myrtle(&stats.myrtle, cfg.write);
+ } else {
+ d_raw((unsigned char *)&stats.myrtle, sizeof(struct sfx_lat_stats_myrtle));
+ }
+ } else {
+ printf("ScaleFlux IO %s Command Latency Statistics Invalid Version Maj %d Min %d\n",
+ cfg.write ? "Write" : "Read", stats.ver.maj, stats.ver.min);
+ }
+ } else if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+int sfx_nvme_get_log(int fd, __u32 nsid, __u8 log_id, __u32 data_len, void *data)
+{
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_get_log_page,
+ .nsid = nsid,
+ .addr = (__u64)(uintptr_t) data,
+ .data_len = data_len,
+ };
+ __u32 numd = (data_len >> 2) - 1;
+ __u16 numdu = numd >> 16, numdl = numd & 0xffff;
+
+ cmd.cdw10 = log_id | (numdl << 16);
+ cmd.cdw11 = numdu;
+
+ return nvme_submit_admin_passthru(fd, &cmd, NULL);
+}
+
+/**
+ * @brief get bb table through admin_passthru
+ *
+ * @param fd
+ * @param buf
+ * @param size
+ *
+ * @return -1 fail ; 0 success
+ */
+static int get_bb_table(int fd, __u32 nsid, unsigned char *buf, __u64 size)
+{
+ if (fd < 0 || !buf || size != 256*4096*sizeof(unsigned char)) {
+ fprintf(stderr, "Invalid Param \r\n");
+ return EINVAL;
+ }
+
+ return sfx_nvme_get_log(fd, nsid, SFX_LOG_BBT, size, (void *)buf);
+}
+
+/**
+ * @brief display bb table
+ *
+ * @param bd_table buffer that contain bb table dumped from drvier
+ * @param table_size buffer size (BYTES), should at least has 8 bytes for mf_bb_count and grown_bb_count
+ */
+static void bd_table_show(unsigned char *bd_table, __u64 table_size)
+{
+ __u32 mf_bb_count = 0;
+ __u32 grown_bb_count = 0;
+ __u32 total_bb_count = 0;
+ __u32 remap_mfbb_count = 0;
+ __u32 remap_gbb_count = 0;
+ __u64 *bb_elem;
+ __u64 *elem_end = (__u64 *)(bd_table + table_size);
+ __u64 i;
+
+ /*buf should at least have 8bytes for mf_bb_count & total_bb_count*/
+ if (!bd_table || table_size < sizeof(__u64))
+ return;
+
+ mf_bb_count = *((__u32 *)bd_table);
+ grown_bb_count = *((__u32 *)(bd_table + sizeof(__u32)));
+ total_bb_count = *((__u32 *)(bd_table + 2 * sizeof(__u32)));
+ remap_mfbb_count = *((__u32 *)(bd_table + 3 * sizeof(__u32)));
+ remap_gbb_count = *((__u32 *)(bd_table + 4 * sizeof(__u32)));
+ 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("REMAP_MFBB_TABLE [");
+ i = 0;
+ while (bb_elem < elem_end && i < remap_mfbb_count) {
+ printf(" 0x%"PRIx64"", (uint64_t)*(bb_elem++));
+ i++;
+ }
+ printf(" ]\n");
+
+ printf("REMAP_GBB_TABLE [");
+ i = 0;
+ while (bb_elem < elem_end && i < remap_gbb_count) {
+ printf(" 0x%"PRIx64"", (uint64_t)*(bb_elem++));
+ i++;
+ }
+ printf(" ]\n");
+}
+
+/**
+ * @brief "hooks of sfx get-bad-block"
+ *
+ * @param argc
+ * @param argv
+ * @param cmd
+ * @param plugin
+ *
+ * @return
+ */
+static int sfx_get_bad_block(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const __u64 buf_size = 256*4096*sizeof(unsigned char);
+ unsigned char *data_buf;
+ struct nvme_dev *dev;
+ int err = 0;
+
+ char *desc = "Get bad block table of sfx block device.";
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ data_buf = malloc(buf_size);
+ if (!data_buf) {
+ fprintf(stderr, "malloc fail, errno %d\r\n", errno);
+ dev_close(dev);
+ return -1;
+ }
+
+ err = get_bb_table(dev_fd(dev), 0xffffffff, data_buf, buf_size);
+ if (err < 0) {
+ perror("get-bad-block");
+ } else if (err != 0) {
+ nvme_show_status(err);
+ } else {
+ bd_table_show(data_buf, buf_size);
+ printf("ScaleFlux get bad block table: success\n");
+ }
+
+ free(data_buf);
+ dev_close(dev);
+ return 0;
+}
+
+static void show_cap_info(struct sfx_freespace_ctx *ctx)
+{
+
+ 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)
+{
+ struct sfx_freespace_ctx ctx = { 0 };
+ char *desc = "query current capacity info";
+ const char *raw = "dump output in binary format";
+ struct nvme_dev *dev;
+ struct config {
+ bool raw_binary;
+ };
+ struct config cfg;
+ int err = 0;
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (nvme_query_cap(dev_fd(dev), 0xffffffff, sizeof(ctx), &ctx)) {
+ perror("sfx-query-cap");
+ err = -1;
+ }
+
+ if (!err) {
+ if (!cfg.raw_binary) {
+ show_cap_info(&ctx);
+ } else {
+ d_raw((unsigned char *)&ctx, sizeof(ctx));
+ }
+ }
+ dev_close(dev);
+ return err;
+}
+
+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;
+ int extend = 0;
+
+ if (nvme_query_cap(fd, 0xffffffff, sizeof(freespace_ctx), &freespace_ctx)) {
+ return -1;
+ }
+
+ /*
+ * 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 (trg_in_4k > ((__u64)provisoned_cap_4k*4)) {
+ fprintf(stderr, "WARNING: the target capacity is too large\n");
+ 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)
+{
+ unsigned 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 = (unsigned char)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)
+{
+ char *desc = "dynamic change capacity";
+ const char *cap_gb = "cap size in GB";
+ const char *cap_byte = "cap size in byte";
+ const char *force = "The \"I know what I'm doing\" flag, skip confirmation before sending command";
+ struct nvme_dev *dev;
+ __u64 cap_in_4k = 0;
+ __u64 cap_in_sec = 0;
+ int shrink = 0;
+ int err = -1;
+
+ struct config {
+ __u64 cap_in_byte;
+ __u32 capacity_in_gb;
+ bool force;
+ };
+
+ struct config cfg = {
+ .cap_in_byte = 0,
+ .capacity_in_gb = 0,
+ .force = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("cap", 'c', &cfg.capacity_in_gb, cap_gb),
+ OPT_SUFFIX("cap-byte", 'z', &cfg.cap_in_byte, cap_byte),
+ OPT_FLAG("force", 'f', &cfg.force, force),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ 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 %"PRIu64"B %"PRIu64" 4K\n",
+ cfg.capacity_in_gb, (uint64_t)cfg.cap_in_byte, (uint64_t)cap_in_4k);
+
+ if (change_sanity_check(dev_fd(dev), cap_in_4k, &shrink)) {
+ printf("ScaleFlux change-capacity: fail\n");
+ dev_close(dev);
+ return err;
+ }
+
+ if (!cfg.force && shrink && !sfx_confirm_change("Changing Cap may irrevocably delete this device's data")) {
+ dev_close(dev);
+ return 0;
+ }
+
+ err = nvme_change_cap(dev_fd(dev), 0xffffffff, cap_in_4k);
+ if (err < 0)
+ perror("sfx-change-cap");
+ else if (err != 0)
+ nvme_show_status(err);
+ else {
+ printf("ScaleFlux change-capacity: success\n");
+ ioctl(dev_fd(dev), BLKRRPART);
+ }
+ dev_close(dev);
+ 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_UP_P_CAP:
+ return "UPDATE_PROVISION_CAPACITY";
+
+ default:
+ return "Unknown";
+ }
+}
+
+static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ char *desc = "ScaleFlux internal set features\n"
+ "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_dev *dev;
+ struct nvme_id_ns ns;
+ int err = 0;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 feature_id;
+ __u32 value;
+ bool 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_FLAG("force", 's', &cfg.force, force),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ 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")) {
+ dev_close(dev);
+ return 0;
+ } else {
+ return sfx_clean_card(dev_fd(dev));
+ }
+
+ }
+
+ if (cfg.feature_id == SFX_FEAT_ATOMIC && cfg.value != 0) {
+ if (cfg.namespace_id != 0xffffffff) {
+ err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id,
+ &ns);
+ if (err) {
+ if (err < 0)
+ perror("identify-namespace");
+ else
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+ }
+ /*
+ * atomic only support with sector-size = 4k now
+ */
+ if ((ns.flbas & 0xf) != 1) {
+ printf("Please change-sector size to 4K, then retry\n");
+ dev_close(dev);
+ return EFAULT;
+ }
+ }
+ } else if (cfg.feature_id == SFX_FEAT_UP_P_CAP) {
+ if (cfg.value <= 0) {
+ fprintf(stderr, "Invalid Param\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ /*Warning for change pacp by GB*/
+ if (!cfg.force && !sfx_confirm_change("Changing physical capacity may irrevocably delete this device's data")) {
+ dev_close(dev);
+ return 0;
+ }
+ }
+
+ err = nvme_sfx_set_features(dev_fd(dev), cfg.namespace_id,
+ cfg.feature_id,
+ cfg.value);
+
+ if (err < 0) {
+ perror("ScaleFlux-set-feature");
+ dev_close(dev);
+ return errno;
+ } else if (!err) {
+ 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)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
+static int sfx_get_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ char *desc = "ScaleFlux internal set features\n"
+ "feature id 1: ATOMIC";
+ const char *feature_id = "hex feature name (required)";
+ const char *namespace_id = "desired namespace";
+ struct nvme_dev *dev;
+ __u32 result = 0;
+ int err = 0;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 feature_id;
+ };
+ struct config cfg = {
+ .namespace_id = 0,
+ .feature_id = 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_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ err = nvme_sfx_get_features(dev_fd(dev), cfg.namespace_id,
+ cfg.feature_id, &result);
+ if (err < 0) {
+ perror("ScaleFlux-get-feature");
+ dev_close(dev);
+ return errno;
+ } else if (!err) {
+ printf("ScaleFlux get-feature:%02x (%s), value:%d\n", cfg.feature_id,
+ sfx_feature_to_string(cfg.feature_id), result);
+ } else if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+
+}
diff --git a/plugins/scaleflux/sfx-nvme.h b/plugins/scaleflux/sfx-nvme.h
new file mode 100644
index 0000000..0b95d92
--- /dev/null
+++ b/plugins/scaleflux/sfx-nvme.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/scaleflux/sfx-nvme
+
+#if !defined(SFX_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define SFX_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("sfx", "ScaleFlux vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve ScaleFlux SMART Log, show it", get_additional_smart_log)
+ ENTRY("lat-stats", "Retrieve ScaleFlux IO Latency Statistics log, show it", get_lat_stats_log)
+ ENTRY("get-bad-block", "Retrieve bad block table of block device, show it", sfx_get_bad_block)
+ 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)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/seagate/seagate-diag.h b/plugins/seagate/seagate-diag.h
new file mode 100644
index 0000000..39f0d39
--- /dev/null
+++ b/plugins/seagate/seagate-diag.h
@@ -0,0 +1,388 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Do NOT modify or remove this copyright and license
+ *
+ * Copyright (c) 2017-2018 Seagate Technology LLC and/or its Affiliates, All Rights Reserved
+ *
+ * ******************************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * \file seagate-diag.h
+ * \brief This file defines the functions and macros to make building a nvme-cli seagate plug-in.
+ *
+ * Author: Debabrata Bardhan <debabrata.bardhan@seagate.com>
+ */
+
+
+#ifndef SEAGATE_NVME_H
+#define SEAGATE_NVME_H
+
+#define SEAGATE_PLUGIN_VERSION_MAJOR 1
+#define SEAGATE_PLUGIN_VERSION_MINOR 2
+
+#define SEAGATE_OCP_PLUGIN_VERSION_MAJOR 1
+#define SEAGATE_OCP_PLUGIN_VERSION_MINOR 0
+
+#define PERSIST_FILE_SIZE (2764800)
+#define ONE_MB (1048576) /* (1024 * 1024) */
+#define PERSIST_CHUNK (65536) /* (1024 * 64) */
+#define FOUR_KB (4096)
+#define STX_NUM_LEGACY_DRV (123)
+
+
+const char* stx_jag_pan_mn[STX_NUM_LEGACY_DRV] = {"ST1000KN0002", "ST1000KN0012", "ST2000KN0002",
+ "ST2000KN0012", "ST4000KN0002", "XP1600HE10002",
+ "XP1600HE10012", "XP1600HE30002", "XP1600HE30012",
+ "XP1920LE10002", "XP1920LE10012", "XP1920LE30002",
+ "XP1920LE30012", "XP3200HE10002", "XP3200HE10012",
+ "XP3840LE10002", "XP3840LE10012", "XP400HE30002",
+ "XP400HE30012", "XP400HE30022", "XP400HE30032",
+ "XP480LE30002", "XP480LE30012", "XP480LE30022",
+ "XP480LE30032", "XP800HE10002", "XP800HE10012",
+ "XP800HE30002", "XP800HE30012", "XP800HE30022",
+ "XP800HE30032", "XP960LE10002", "XP960LE10012",
+ "XP960LE30002", "XP960LE30012", "XP960LE30022",
+ "XP960LE30032", "XP256LE30011", "XP256LE30021",
+ "XP7680LE80002", "XP7680LE80003", "XP15360LE80003",
+ "XP30720LE80003", "XP7200-1A2048", "XP7200-1A4096",
+ "XP7201-2A2048", "XP7201-2A4096", "XP7200-1A8192",
+ "ST1000HM0021", "ST1000HM0031", "ST1000HM0061",
+ "ST1000HM0071", "ST1000HM0081", "ST1200HM0001",
+ "ST1600HM0031", "ST1800HM0001", "ST1800HM0011",
+ "ST2000HM0011", "ST2000HM0031", "ST400HM0061",
+ "ST400HM0071", "ST500HM0021", "ST500HM0031",
+ "ST500HM0061", "ST500HM0071", "ST500HM0081",
+ "ST800HM0061", "ST800HM0071", "ST1600HM0011",
+ "ST1600KN0001", "ST1600KN0011", "ST1920HM0001",
+ "ST1920KN0001", "ST1920KN0011", "ST400HM0021",
+ "ST400KN0001", "ST400KN0011", "ST480HM0001",
+ "ST480KN0001", "ST480KN0011", "ST800HM0021",
+ "ST800KN0001", "ST800KN0011", "ST960HM0001",
+ "ST960KN0001", "ST960KN0011", "XF1441-1AA251024",
+ "XF1441-1AA252048", "XF1441-1AA25512", "XF1441-1AB251024",
+ "XF1441-1AB252048", "XF1441-1AB25512", "XF1441-1BA251024",
+ "XF1441-1BA252048", "XF1441-1BA25512", "XF1441-1BB251024",
+ "XF1441-1BB252048", "XF1441-1BB25512", "ST400HM0031",
+ "ST400KN0021", "ST400KN0031", "ST480HM0011",
+ "ST480KN0021", "ST480KN0031", "ST800HM0031",
+ "ST800KN0021", "ST800KN0031", "ST960HM0011",
+ "ST960KN0021", "ST960KN0031", "XM1441-1AA111024",
+ "XM1441-1AA112048", "XM1441-1AA11512", "XM1441-1AA801024",
+ "XM1441-1AA80512", "XM1441-1AB111024", "XM1441-1AB112048",
+ "XM1441-1BA111024", "XM1441-1BA112048", "XM1441-1BA11512",
+ "XM1441-1BA801024", "XM1441-1BA80512", "XM1441-1BB112048"};
+
+
+/***************************
+*Supported Log-Pages from FW
+***************************/
+
+typedef struct __attribute__((__packed__)) log_page_map_entry {
+ __u32 LogPageID;
+ __u32 LogPageSignature;
+ __u32 LogPageVersion;
+} log_page_map_entry;
+
+#define MAX_SUPPORTED_LOG_PAGE_ENTRIES ((4096 - sizeof(__u32)) / sizeof(log_page_map_entry))
+
+typedef struct __attribute__((__packed__)) log_page_map {
+ __u32 NumLogPages;
+ log_page_map_entry LogPageEntry[ MAX_SUPPORTED_LOG_PAGE_ENTRIES ];
+} log_page_map;
+/* EOF Supported Log-Pages from FW */
+
+
+/***************************
+* Extended-SMART Information
+***************************/
+#define NUMBER_EXTENDED_SMART_ATTRIBUTES 42
+
+typedef enum _EXTENDED_SMART_VERSION_
+{
+ EXTENDED_SMART_VERSION_NONE,
+ EXTENDED_SMART_VERSION_GEN,
+ EXTENDED_SMART_VERSION_VENDOR1,
+} EXTENDED_SMART_VERSION;
+
+typedef struct __attribute__((__packed__)) _SmartVendorSpecific
+{
+ __u8 AttributeNumber;
+ __u16 SmartStatus;
+ __u8 NominalValue;
+ __u8 LifetimeWorstValue;
+ __u32 Raw0_3;
+ __u8 RawHigh[3];
+} SmartVendorSpecific;
+
+typedef struct __attribute__((__packed__)) _EXTENDED_SMART_INFO_T
+{
+ __u16 Version;
+ SmartVendorSpecific vendorData[NUMBER_EXTENDED_SMART_ATTRIBUTES];
+ __u8 vendor_specific_reserved[6];
+} EXTENDED_SMART_INFO_T;
+
+typedef struct __attribute__((__packed__)) vendor_smart_attribute_data
+{
+ __u8 AttributeNumber;
+ __u8 Rsvd[3];
+ __u32 LSDword;
+ __u32 MSDword;
+} vendor_smart_attribute_data;
+
+struct __attribute__((__packed__)) nvme_temetry_log_hdr
+{
+ __u8 log_id;
+ __u8 rsvd1[4];
+ __u8 ieee_id[3];
+ __le16 tele_data_area1;
+ __le16 tele_data_area2;
+ __le16 tele_data_area3;
+ __u8 rsvd14[368];
+ __u8 tele_data_aval;
+ __u8 tele_data_gen_num;
+ __u8 reason_identifier[128];
+};
+
+typedef struct __attribute__((__packed__)) _U128
+{
+ __u64 LS__u64;
+ __u64 MS__u64;
+} U128;
+
+typedef struct __attribute__((__packed__)) _vendor_log_page_CF_Attr
+{
+ __u16 SuperCapCurrentTemperature;
+ __u16 SuperCapMaximumTemperature;
+ __u8 SuperCapStatus;
+ __u8 Reserved5to7[3];
+ U128 DataUnitsReadToDramNamespace;
+ U128 DataUnitsWrittenToDramNamespace;
+ __u64 DramCorrectableErrorCount;
+ __u64 DramUncorrectableErrorCount;
+} vendor_log_page_CF_Attr;
+
+typedef struct __attribute__((__packed__)) _vendor_log_page_CF
+{
+ vendor_log_page_CF_Attr AttrCF;
+ __u8 Vendor_Specific_Reserved[ 456 ]; /* 56-511 */
+} vendor_log_page_CF;
+
+typedef struct __attribute__((__packed__)) _STX_EXT_SMART_LOG_PAGE_C0
+{
+ U128 phyMediaUnitsWrt;
+ U128 phyMediaUnitsRd;
+ __u64 badUsrNandBlocks;
+ __u64 badSysNandBlocks;
+ __u64 xorRecoveryCnt;
+ __u64 ucRdEc;
+ __u64 softEccEc;
+ __u64 etoeCrrCnt;
+ __u64 sysDataUsed : 8;
+ __u64 refreshCount : 56;
+ __u64 usrDataEraseCnt;
+ __u16 thermalThrottling;
+ __u8 dssdSpecVerErrata;
+ __u16 dssdSpecVerPoint;
+ __u16 dssdSpecVerMinor;
+ __u8 dssdSpecVerMajor;
+ __u64 pcieCorrEc;
+ __u32 incompleteShutdowns;
+ __u32 rsvd_116_119;
+ __u8 freeBlocks;
+ __u8 rsvd_121_127[7];
+ __u16 capHealth;
+ __u8 nvmeErrataVer;
+ __u8 rsvd_131_135[5];
+ __u64 unalignedIO;
+ __u64 secVerNum;
+ __u64 totalNUSE;
+ U128 plpStartCnt;
+ U128 enduranceEstimate;
+ __u64 pcieLinkRetCnt;
+ __u64 powStateChangeCnt;
+ __u8 rsvd_208_493[286];
+ __u16 logPageVer;
+ U128 logPageGUID;
+} STX_EXT_SMART_LOG_PAGE_C0;
+
+/* EOF Extended-SMART Information*/
+
+
+/**************************
+* PCIE ERROR INFORMATION
+**************************/
+typedef struct __attribute__((__packed__)) pcie_error_log_page
+{
+ __u32 Version;
+ __u32 BadDllpErrCnt;
+ __u32 BadTlpErrCnt;
+ __u32 RcvrErrCnt;
+ __u32 ReplayTOErrCnt;
+ __u32 ReplayNumRolloverErrCnt;
+ __u32 FCProtocolErrCnt;
+ __u32 DllpProtocolErrCnt;
+ __u32 CmpltnTOErrCnt;
+ __u32 RcvrQOverflowErrCnt;
+ __u32 UnexpectedCplTlpErrCnt;
+ __u32 CplTlpURErrCnt;
+ __u32 CplTlpCAErrCnt;
+ __u32 ReqCAErrCnt;
+ __u32 ReqURErrCnt;
+ __u32 EcrcErrCnt;
+ __u32 MalformedTlpErrCnt;
+ __u32 CplTlpPoisonedErrCnt;
+ __u32 MemRdTlpPoisonedErrCnt;
+} pcie_error_log_page;
+/*EOF PCIE ERROR INFORMATION */
+
+/**************************************
+* FW Activation History Log INFORMATION
+***************************************/
+
+typedef struct __attribute__((__packed__)) _stx_fw_activ_his_ele
+{
+ __u8 entryVerNum;
+ __u8 entryLen;
+ __u16 rev02_03;
+ __u16 fwActivCnt;
+ __u64 timeStamp;
+ __u64 rev14_21;
+ __u64 powCycleCnt;
+ __u8 previousFW[8];
+ __u8 newFW[8];
+ __u8 slotNum;
+ __u8 commitActionType;
+ __u16 result;
+ __u8 rev50_63[14];
+} stx_fw_activ_his_ele;
+
+typedef struct __attribute__((__packed__)) _stx_fw_activ_history_log_page
+{
+ __u8 logID;
+ __u8 rev01_03[3];
+ __u32 numValidFwActHisEnt;
+ stx_fw_activ_his_ele fwActHisEnt[20];
+ __u8 rev1288_4077[2790];
+ __u16 logPageVer;
+ __u8 logPageGUID[16];
+} stx_fw_activ_history_log_page;
+
+/* FW Activation History Log INFORMATION */
+
+
+typedef enum
+{
+ VS_ATTR_SOFT_READ_ERROR_RATE, /* 0 OFFSET : 02 -13 bytes */
+ VS_ATTR_REALLOCATED_SECTOR_COUNT, /* 1 OFFSET : 14 -25 bytes */
+ VS_ATTR_POWER_ON_HOURS, /* 2 OFFSET : 26 -37 bytes */
+ VS_ATTR_POWER_FAIL_EVENT_COUNT, /* 3 OFFSET : 38 -49 bytes */
+ VS_ATTR_DEVICE_POWER_CYCLE_COUNT, /* 4 OFFSET : 50 -61 bytes */
+ VS_ATTR_GB_ERASED, /* 5 OFFSET : 62 -73 bytes */
+ VS_ATTR_LIFETIME_DEVSLEEP_EXIT_COUNT, /* 6 OFFSET : 74 -85 bytes */
+ VS_ATTR_LIFETIME_ENTERING_PS4_COUNT, /* 7 OFFSET : 86 -97 bytes */
+ VS_ATTR_LIFETIME_ENTERING_PS3_COUNT, /* 8 OFFSET : 98 -109 bytes */
+ VS_ATTR_RETIRED_BLOCK_COUNT, /* 9 OFFSET : 110 -121 bytes */
+ VS_ATTR_PROGRAM_FAILURE_COUNT, /* 10 OFFSET : 122 -133 bytes */
+ VS_ATTR_ERASE_FAIL_COUNT, /* 11 OFFSET : 134 -145 bytes */
+ VS_ATTR_AVG_ERASE_COUNT, /* 12 OFFSET : 146 -157 bytes */
+ VS_ATTR_UNEXPECTED_POWER_LOSS_COUNT, /* 13 OFFSET : 158 -169 bytes */
+ VS_ATTR_WEAR_RANGE_DELTA, /* 14 OFFSET : 170 -181 bytes */
+ VS_ATTR_SATA_INTERFACE_DOWNSHIFT_COUNT, /* 15 OFFSET : 182 -193 bytes */
+ VS_ATTR_END_TO_END_CRC_ERROR_COUNT, /* 16 OFFSET : 194 -205 bytes */
+ VS_ATTR_MAX_LIFE_TEMPERATURE, /* 17 OFFSET : 206 -217 bytes */
+ VS_ATTR_UNCORRECTABLE_RAISE_ERRORS, /* 18 OFFSET : 218 -229 bytes */
+ VS_ATTR_DRIVE_LIFE_PROTECTION_STATUS, /* 19 OFFSET : 230 -241 bytes */
+ VS_ATTR_REMAINING_SSD_LIFE, /* 20 OFFSET : 242 -253 bytes */
+ VS_ATTR_LIFETIME_WRITES_TO_FLASH, /* 21 OFFSET : 254 -265 bytes */
+ VS_ATTR_LIFETIME_WRITES_FROM_HOST, /* 22 OFFSET : 266 -277 bytes */
+ VS_ATTR_LIFETIME_READS_TO_HOST, /* 23 OFFSET : 278 -289 bytes */
+ VS_ATTR_FREE_SPACE, /* 24 OFFSET : 290 -301 bytes */
+ VS_ATTR_TRIM_COUNT_LSB, /* 25 OFFSET : 302 -313 bytes */
+ VS_ATTR_TRIM_COUNT_MSB, /* 26 OFFSET : 314 -325 bytes */
+ VS_ATTR_OP_PERCENTAGE, /* 27 OFFSET : 326 -337 bytes */
+ VS_ATTR_RAISE_ECC_CORRECTABLE_ERROR_COUNT, /* 28 OFFSET : 338 -349 bytes */
+ VS_ATTR_UNCORRECTABLE_ECC_ERRORS , /* 29 OFFSET : 350 -361 bytes */
+ VS_ATTR_LIFETIME_WRITES0_TO_FLASH, /* 30 OFFSET : 362-372 bytes */
+ VS_ATTR_LIFETIME_WRITES1_TO_FLASH, /* 31 OFFSET : 374-385 bytes */
+ VS_ATTR_LIFETIME_WRITES0_FROM_HOST, /* 32 OFFSET : 386-397 bytes */
+ VS_ATTR_LIFETIME_WRITES1_FROM_HOST, /* 33 OFFSET : 398-409 bytes */
+ VS_ATTR_LIFETIME_READ0_FROM_HOST, /* 34 OFFSET : 410-421 bytes */
+ VS_ATTR_LIFETIME_READ1_FROM_HOST, /* 35 OFFSET : 422-433 bytes */
+ VS_ATTR_PCIE_PHY_CRC_ERROR, /* 36 OFFSET : 434-445 bytes */
+ VS_ATTR_BAD_BLOCK_COUNT_SYSTEM, /* 37 OFFSET : 446-457 bytes */
+ VS_ATTR_BAD_BLOCK_COUNT_USER, /* 38 OFFSET : 458-469 bytes */
+ VS_ATTR_THERMAL_THROTTLING_STATUS, /* 39 OFFSET : 470-481 bytes */
+ VS_ATTR_POWER_CONSUMPTION, /* 40 OFFSET : 482-493 bytes */
+ VS_ATTR_MAX_SOC_LIFE_TEMPERATURE, /* 41 OFFSET : 494-505 bytes */
+ VS_MAX_ATTR_NUMBER
+} extended_smart_attributes;
+
+/*Smart attribute IDs */
+
+typedef enum
+{
+ VS_ATTR_ID_SOFT_READ_ERROR_RATE = 1,
+ VS_ATTR_ID_REALLOCATED_SECTOR_COUNT = 5,
+ VS_ATTR_ID_POWER_ON_HOURS = 9,
+ VS_ATTR_ID_POWER_FAIL_EVENT_COUNT = 11,
+ VS_ATTR_ID_DEVICE_POWER_CYCLE_COUNT = 12,
+ VS_ATTR_ID_RAW_READ_ERROR_RATE = 13,
+ VS_ATTR_ID_GROWN_BAD_BLOCK_COUNT = 40,
+ VS_ATTR_ID_END_2_END_CORRECTION_COUNT = 41,
+ VS_ATTR_ID_MIN_MAX_WEAR_RANGE_COUNT = 42,
+ VS_ATTR_ID_REFRESH_COUNT = 43,
+ VS_ATTR_ID_BAD_BLOCK_COUNT_USER = 44,
+ VS_ATTR_ID_BAD_BLOCK_COUNT_SYSTEM = 45,
+ VS_ATTR_ID_THERMAL_THROTTLING_STATUS = 46,
+ VS_ATTR_ID_ALL_PCIE_CORRECTABLE_ERROR_COUNT = 47,
+ VS_ATTR_ID_ALL_PCIE_UNCORRECTABLE_ERROR_COUNT = 48,
+ VS_ATTR_ID_INCOMPLETE_SHUTDOWN_COUNT = 49,
+ VS_ATTR_ID_GB_ERASED_LSB = 100,
+ VS_ATTR_ID_GB_ERASED_MSB = 101,
+ VS_ATTR_ID_LIFETIME_ENTERING_PS4_COUNT = 102,
+ VS_ATTR_ID_LIFETIME_ENTERING_PS3_COUNT = 103,
+ VS_ATTR_ID_LIFETIME_DEVSLEEP_EXIT_COUNT = 104,
+ VS_ATTR_ID_RETIRED_BLOCK_COUNT = 170,
+ VS_ATTR_ID_PROGRAM_FAILURE_COUNT = 171,
+ VS_ATTR_ID_ERASE_FAIL_COUNT = 172,
+ VS_ATTR_ID_AVG_ERASE_COUNT = 173,
+ VS_ATTR_ID_UNEXPECTED_POWER_LOSS_COUNT = 174,
+ VS_ATTR_ID_WEAR_RANGE_DELTA = 177,
+ VS_ATTR_ID_SATA_INTERFACE_DOWNSHIFT_COUNT = 183,
+ VS_ATTR_ID_END_TO_END_CRC_ERROR_COUNT = 184,
+ VS_ATTR_ID_UNCORRECTABLE_READ_ERRORS = 188,
+ VS_ATTR_ID_MAX_LIFE_TEMPERATURE = 194,
+ VS_ATTR_ID_RAISE_ECC_CORRECTABLE_ERROR_COUNT = 195,
+ VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS = 198,
+ VS_ATTR_ID_DRIVE_LIFE_PROTECTION_STATUS = 230,
+ VS_ATTR_ID_REMAINING_SSD_LIFE = 231,
+ VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB = 233,
+ VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB = 234,
+ VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB = 241,
+ VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB = 242,
+ VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB = 243,
+ VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB = 244,
+ VS_ATTR_ID_FREE_SPACE = 245,
+ VS_ATTR_ID_TRIM_COUNT_LSB = 250,
+ VS_ATTR_ID_TRIM_COUNT_MSB = 251,
+ VS_ATTR_ID_OP_PERCENTAGE = 252,
+ VS_ATTR_ID_MAX_SOC_LIFE_TEMPERATURE = 253,
+} smart_attributes_ids;
+
+#define TELEMETRY_BLOCKS_TO_READ 8
+
+void seaget_d_raw(unsigned char *buf, int len, int fd);
+
+
+#define DP_CLASS_ID_FULL 0
+
+#endif
diff --git a/plugins/seagate/seagate-nvme.c b/plugins/seagate/seagate-nvme.c
new file mode 100644
index 0000000..1bd30a5
--- /dev/null
+++ b/plugins/seagate/seagate-nvme.c
@@ -0,0 +1,2042 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Do NOT modify or remove this copyright and license
+ *
+ * Copyright (c) 2017-2018 Seagate Technology LLC and/or its Affiliates, All Rights Reserved
+ *
+ * ******************************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * \file seagate-nvme.c
+ * \brief This file defines the functions and macros to make building a nvme-cli seagate plug-in.
+ *
+ * Author: Debabrata Bardhan <debabrata.bardhan@seagate.com>
+ */
+
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+#include <time.h>
+
+#define CREATE_CMD
+
+#include "seagate-nvme.h"
+#include "seagate-diag.h"
+
+
+/***************************************
+ * Command for "log-pages-supp"
+ ***************************************/
+static char *log_pages_supp_print(__u32 pageID)
+{
+ switch (pageID) {
+ case 0x01:
+ return "ERROR_INFORMATION";
+ break;
+ case 0x02:
+ return "SMART_INFORMATION";
+ break;
+ case 0x03:
+ return "FW_SLOT_INFORMATION";
+ break;
+ case 0x04:
+ return "CHANGED_NAMESPACE_LIST";
+ break;
+ case 0x05:
+ return "COMMANDS_SUPPORTED_AND_EFFECTS";
+ break;
+ case 0x06:
+ return "DEVICE_SELF_TEST";
+ break;
+ case 0x07:
+ return "TELEMETRY_HOST_INITIATED";
+ break;
+ case 0x08:
+ return "TELEMETRY_CONTROLLER_INITIATED";
+ break;
+ case 0xC0:
+ return "VS_MEDIA_SMART_LOG";
+ break;
+ case 0xC1:
+ return "VS_DEBUG_LOG1";
+ break;
+ case 0xC2:
+ return "VS_SEC_ERROR_LOG_PAGE";
+ break;
+ case 0xC3:
+ return "VS_LIFE_TIME_DRIVE_HISTORY";
+ break;
+ case 0xC4:
+ return "VS_EXTENDED_SMART_INFO";
+ break;
+ case 0xC5:
+ return "VS_LIST_SUPPORTED_LOG_PAGE";
+ break;
+ case 0xC6:
+ return "VS_POWER_MONITOR_LOG_PAGE";
+ break;
+ case 0xC7:
+ return "VS_CRITICAL_EVENT_LOG_PAGE";
+ break;
+ case 0xC8:
+ return "VS_RECENT_DRIVE_HISTORY";
+ break;
+ case 0xC9:
+ return "VS_SEC_ERROR_LOG_PAGE";
+ break;
+ case 0xCA:
+ return "VS_LIFE_TIME_DRIVE_HISTORY";
+ break;
+ case 0xCB:
+ return "VS_PCIE_ERROR_LOG_PAGE";
+ break;
+ case 0xCF:
+ return "DRAM Supercap SMART Attributes";
+ break;
+ case 0xD6:
+ return "VS_OEM2_WORK_LOAD";
+ break;
+ case 0xD7:
+ return "VS_OEM2_FW_SECURITY";
+ break;
+ case 0xD8:
+ return "VS_OEM2_REVISION";
+ break;
+ default:
+ return "UNKNOWN";
+ break;
+ }
+}
+
+static int stx_is_jag_pan(char *devMN)
+{
+ int match_found = 1; /* found = 0, not_found = 1 */
+
+ for (int i = 0; i < STX_NUM_LEGACY_DRV; i++) {
+ match_found = strncmp(devMN, stx_jag_pan_mn[i], strlen(stx_jag_pan_mn[i]));
+ if (match_found == 0) {
+ break;
+ }
+ }
+
+ return match_found;
+}
+
+
+static void json_log_pages_supp(log_page_map *logPageMap)
+{
+ struct json_object *root;
+ struct json_object *logPages;
+ __u32 i = 0;
+
+ root = json_create_object();
+ logPages = json_create_array();
+ json_object_add_value_array(root, "supported_log_pages", logPages);
+
+ for (i = 0; i < le32_to_cpu(logPageMap->NumLogPages); i++) {
+ struct json_object *lbaf = json_create_object();
+ json_object_add_value_int(lbaf, "logpage_id",
+ le32_to_cpu(logPageMap->LogPageEntry[i].LogPageID));
+ json_object_add_value_string(lbaf, "logpage_name",
+ log_pages_supp_print(le32_to_cpu(logPageMap->LogPageEntry[i].LogPageID)));
+
+ json_array_add_value_object(logPages, lbaf);
+ }
+ json_print_object(root, NULL);
+ json_free_object(root);
+}
+
+static int log_pages_supp(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = 0;
+ __u32 i = 0;
+ log_page_map logPageMap;
+ const char *desc = "Retrieve Seagate Supported Log-Page information for the given device ";
+ const char *output_format = "output in binary format";
+ struct nvme_dev *dev;
+ int fmt;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+ err = nvme_get_log_simple(dev_fd(dev), 0xc5,
+ sizeof(logPageMap), &logPageMap);
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ printf ("Seagate Supported Log-pages count :%d\n",
+ le32_to_cpu(logPageMap.NumLogPages));
+ printf ("%-15s %-30s\n", "LogPage-Id", "LogPage-Name");
+
+ for (fmt = 0; fmt < 45; fmt++)
+ printf("-");
+ printf("\n");
+ } else
+ json_log_pages_supp(&logPageMap);
+
+ for (i = 0; i < le32_to_cpu(logPageMap.NumLogPages); i++) {
+ if (strcmp(cfg.output_format, "json")) {
+ printf("0x%-15X",
+ le32_to_cpu(logPageMap.LogPageEntry[i].LogPageID));
+ printf("%-30s\n",
+ log_pages_supp_print(le32_to_cpu(logPageMap.LogPageEntry[i].LogPageID)));
+ }
+ }
+ }
+
+ if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+/* EOF Command for "log-pages-supp" */
+
+
+/***************************************
+* Extended-SMART Information
+***************************************/
+static char *print_ext_smart_id(__u8 attrId)
+{
+ switch (attrId) {
+ case VS_ATTR_ID_SOFT_READ_ERROR_RATE:
+ return "Soft ECC error count";
+ break;
+ case VS_ATTR_ID_REALLOCATED_SECTOR_COUNT:
+ return "Bad NAND block count";
+ break;
+ case VS_ATTR_ID_POWER_ON_HOURS:
+ return "Power On Hours";
+ break;
+ case VS_ATTR_ID_POWER_FAIL_EVENT_COUNT:
+ return "Power Fail Event Count";
+ break;
+ case VS_ATTR_ID_DEVICE_POWER_CYCLE_COUNT:
+ return "Device Power Cycle Count";
+ break;
+ case VS_ATTR_ID_RAW_READ_ERROR_RATE:
+ return "Raw Read Error Count";
+ break;
+ case VS_ATTR_ID_GROWN_BAD_BLOCK_COUNT:
+ return "Bad NAND block count";
+ break;
+ case VS_ATTR_ID_END_2_END_CORRECTION_COUNT:
+ return "SSD End to end correction counts";
+ break;
+ case VS_ATTR_ID_MIN_MAX_WEAR_RANGE_COUNT:
+ return "User data erase counts";
+ break;
+ case VS_ATTR_ID_REFRESH_COUNT:
+ return "Refresh count";
+ break;
+ case VS_ATTR_ID_BAD_BLOCK_COUNT_USER:
+ return "User data erase fail count";
+ break;
+ case VS_ATTR_ID_BAD_BLOCK_COUNT_SYSTEM:
+ return "System area erase fail count";
+ break;
+ case VS_ATTR_ID_THERMAL_THROTTLING_STATUS:
+ return "Thermal throttling status and count";
+ break;
+ case VS_ATTR_ID_ALL_PCIE_CORRECTABLE_ERROR_COUNT:
+ return "PCIe Correctable Error count";
+ break;
+ case VS_ATTR_ID_ALL_PCIE_UNCORRECTABLE_ERROR_COUNT:
+ return "PCIe Uncorrectable Error count";
+ break;
+ case VS_ATTR_ID_INCOMPLETE_SHUTDOWN_COUNT:
+ return "Incomplete shutdowns";
+ break;
+ case VS_ATTR_ID_GB_ERASED_LSB:
+ return "LSB of Flash GB erased";
+ break;
+ case VS_ATTR_ID_GB_ERASED_MSB:
+ return "MSB of Flash GB erased";
+ break;
+ case VS_ATTR_ID_LIFETIME_DEVSLEEP_EXIT_COUNT:
+ return "LIFETIME_DEV_SLEEP_EXIT_COUNT";
+ break;
+ case VS_ATTR_ID_LIFETIME_ENTERING_PS4_COUNT:
+ return "LIFETIME_ENTERING_PS4_COUNT";
+ break;
+ case VS_ATTR_ID_LIFETIME_ENTERING_PS3_COUNT:
+ return "LIFETIME_ENTERING_PS3_COUNT";
+ break;
+ case VS_ATTR_ID_RETIRED_BLOCK_COUNT:
+ return "Retired block count";
+ break;
+ case VS_ATTR_ID_PROGRAM_FAILURE_COUNT:
+ return "Program fail count";
+ break;
+ case VS_ATTR_ID_ERASE_FAIL_COUNT:
+ return "Erase Fail Count";
+ break;
+ case VS_ATTR_ID_AVG_ERASE_COUNT:
+ return "System data % used";
+ break;
+ case VS_ATTR_ID_UNEXPECTED_POWER_LOSS_COUNT:
+ return "Unexpected power loss count";
+ break;
+ case VS_ATTR_ID_WEAR_RANGE_DELTA:
+ return "Wear range delta";
+ break;
+ case VS_ATTR_ID_SATA_INTERFACE_DOWNSHIFT_COUNT:
+ return "PCIE_INTF_DOWNSHIFT_COUNT";
+ break;
+ case VS_ATTR_ID_END_TO_END_CRC_ERROR_COUNT:
+ return "E2E_CRC_ERROR_COUNT";
+ break;
+ case VS_ATTR_ID_UNCORRECTABLE_READ_ERRORS:
+ return "Uncorrectable Read Error Count";
+ break;
+ case VS_ATTR_ID_MAX_LIFE_TEMPERATURE:
+ return "Max lifetime temperature";
+ break;
+ case VS_ATTR_ID_RAISE_ECC_CORRECTABLE_ERROR_COUNT:
+ return "RAIS_ECC_CORRECT_ERR_COUNT";
+ break;
+ case VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS:
+ return "Uncorrectable RAISE error count";
+ break;
+ case VS_ATTR_ID_DRIVE_LIFE_PROTECTION_STATUS:
+ return "DRIVE_LIFE_PROTECTION_STATUS";
+ break;
+ case VS_ATTR_ID_REMAINING_SSD_LIFE:
+ return "Remaining SSD life";
+ break;
+ case VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB:
+ return "LSB of Physical (NAND) bytes written";
+ break;
+ case VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB:
+ return "MSB of Physical (NAND) bytes written";
+ break;
+ case VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB:
+ return "LSB of Physical (HOST) bytes written";
+ break;
+ case VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB:
+ return "MSB of Physical (HOST) bytes written";
+ break;
+ case VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB:
+ return "LSB of Physical (NAND) bytes read";
+ break;
+ case VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB:
+ return "MSB of Physical (NAND) bytes read";
+ break;
+ case VS_ATTR_ID_FREE_SPACE:
+ return "Free Space";
+ break;
+ case VS_ATTR_ID_TRIM_COUNT_LSB:
+ return "LSB of Trim count";
+ break;
+ case VS_ATTR_ID_TRIM_COUNT_MSB:
+ return "MSB of Trim count";
+ break;
+ case VS_ATTR_ID_OP_PERCENTAGE:
+ return "OP percentage";
+ break;
+ case VS_ATTR_ID_MAX_SOC_LIFE_TEMPERATURE:
+ return "Max lifetime SOC temperature";
+ break;
+ default:
+ return "Un-Known";
+ }
+}
+
+static __u64 smart_attribute_vs(__u16 verNo, SmartVendorSpecific attr)
+{
+ __u64 val = 0;
+ vendor_smart_attribute_data *attrVendor;
+
+ /**
+ * These are all Vendor A specific attributes.
+ */
+ if (verNo >= EXTENDED_SMART_VERSION_VENDOR1) {
+ attrVendor = (vendor_smart_attribute_data *)&attr;
+ memcpy(&val, &(attrVendor->LSDword), sizeof(val));
+ return val;
+ } else
+ return le32_to_cpu(attr.Raw0_3);
+}
+
+static void print_smart_log(__u16 verNo, SmartVendorSpecific attr, int lastAttr)
+{
+ static __u64 lsbGbErased = 0, msbGbErased = 0, lsbLifWrtToFlash = 0, msbLifWrtToFlash = 0,
+ lsbLifWrtFrmHost = 0, msbLifWrtFrmHost = 0, lsbLifRdToHost = 0, msbLifRdToHost = 0, lsbTrimCnt = 0, msbTrimCnt = 0;
+ char buf[40] = {0};
+ char strBuf[35] = {0};
+ int hideAttr = 0;
+
+ if (attr.AttributeNumber == VS_ATTR_ID_GB_ERASED_LSB) {
+ lsbGbErased = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_GB_ERASED_MSB) {
+ msbGbErased = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB) {
+ lsbLifWrtToFlash = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB) {
+ msbLifWrtToFlash = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB) {
+ lsbLifWrtFrmHost = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB) {
+ msbLifWrtFrmHost = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB) {
+ lsbLifRdToHost = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB) {
+ msbLifRdToHost = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_TRIM_COUNT_LSB) {
+ lsbTrimCnt = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if (attr.AttributeNumber == VS_ATTR_ID_TRIM_COUNT_MSB) {
+ msbTrimCnt = smart_attribute_vs(verNo, attr);
+ hideAttr = 1;
+ }
+
+ if ((attr.AttributeNumber != 0) && (hideAttr != 1)) {
+ printf("%-40s", print_ext_smart_id(attr.AttributeNumber));
+ printf("%-15d", attr.AttributeNumber);
+ printf(" 0x%016"PRIx64"\n", (uint64_t)smart_attribute_vs(verNo, attr));
+ }
+
+ if (lastAttr == 1) {
+
+ sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_GB_ERASED_LSB) + 7));
+ printf("%-40s", strBuf);
+
+ printf("%-15d", VS_ATTR_ID_GB_ERASED_MSB << 8 | VS_ATTR_ID_GB_ERASED_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbGbErased, (uint64_t)lsbGbErased);
+ printf(" %s\n", buf);
+
+ sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB) + 7));
+ printf("%-40s", strBuf);
+
+ printf("%-15d", VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtToFlash, (uint64_t)lsbLifWrtToFlash);
+ printf(" %s\n", buf);
+
+ sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB) + 7));
+ printf("%-40s", strBuf);
+
+ printf("%-15d", VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtFrmHost, (uint64_t)lsbLifWrtFrmHost);
+ printf(" %s\n", buf);
+
+ sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB) + 7));
+ printf("%-40s", strBuf);
+
+ printf("%-15d", VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifRdToHost, (uint64_t)lsbLifRdToHost);
+ printf(" %s\n", buf);
+
+ sprintf(strBuf, "%s", (print_ext_smart_id(VS_ATTR_ID_TRIM_COUNT_LSB) + 7));
+ printf("%-40s", strBuf);
+
+ printf("%-15d", VS_ATTR_ID_TRIM_COUNT_MSB << 8 | VS_ATTR_ID_TRIM_COUNT_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbTrimCnt, (uint64_t)lsbTrimCnt);
+ printf(" %s\n", buf);
+
+ }
+}
+
+static void json_print_smart_log(struct json_object *root, EXTENDED_SMART_INFO_T *ExtdSMARTInfo)
+{
+ struct json_object *lbafs;
+ int index = 0;
+
+ static __u64 lsbGbErased = 0, msbGbErased = 0, lsbLifWrtToFlash = 0, msbLifWrtToFlash = 0,
+ lsbLifWrtFrmHost = 0, msbLifWrtFrmHost = 0, lsbLifRdToHost = 0, msbLifRdToHost = 0, lsbTrimCnt = 0, msbTrimCnt = 0;
+ char buf[40] = {0};
+
+ lbafs = json_create_array();
+ json_object_add_value_array(root, "Extended-SMART-Attributes", lbafs);
+
+ for (index = 0; index < NUMBER_EXTENDED_SMART_ATTRIBUTES; index++) {
+ struct json_object *lbaf = json_create_object();
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber != 0) {
+ json_object_add_value_string(lbaf, "attribute_name", print_ext_smart_id(ExtdSMARTInfo->vendorData[index].AttributeNumber));
+ json_object_add_value_int(lbaf, "attribute_id", ExtdSMARTInfo->vendorData[index].AttributeNumber);
+ json_object_add_value_int(lbaf, "attribute_value", smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]));
+ json_array_add_value_object(lbafs, lbaf);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_GB_ERASED_LSB)
+ lsbGbErased = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_GB_ERASED_MSB)
+ msbGbErased = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB)
+ lsbLifWrtToFlash = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB)
+ msbLifWrtToFlash = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB)
+ lsbLifWrtFrmHost = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB)
+ msbLifWrtFrmHost = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB)
+ lsbLifRdToHost = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB)
+ msbLifRdToHost = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_TRIM_COUNT_LSB)
+ lsbTrimCnt = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+
+ if (ExtdSMARTInfo->vendorData[index].AttributeNumber == VS_ATTR_ID_TRIM_COUNT_MSB)
+ msbTrimCnt = smart_attribute_vs(ExtdSMARTInfo->Version, ExtdSMARTInfo->vendorData[index]);
+ }
+ }
+
+ struct json_object *lbaf = json_create_object();
+
+ json_object_add_value_string(lbaf, "attribute_name", (print_ext_smart_id(VS_ATTR_ID_GB_ERASED_LSB) + 7));
+
+ json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_GB_ERASED_MSB << 8 | VS_ATTR_ID_GB_ERASED_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbGbErased, (uint64_t)lsbGbErased);
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(lbafs, lbaf);
+
+
+ lbaf = json_create_object();
+
+ json_object_add_value_string(lbaf, "attribute_name", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB) + 7));
+
+ json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_TO_FLASH_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtToFlash, (uint64_t)lsbLifWrtToFlash);
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(lbafs, lbaf);
+
+
+ lbaf = json_create_object();
+
+ json_object_add_value_string(lbaf, "attribute_name", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB) + 7));
+
+ json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_WRITES_FROM_HOST_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifWrtFrmHost, (uint64_t)lsbLifWrtFrmHost);
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(lbafs, lbaf);
+
+
+ lbaf = json_create_object();
+
+ json_object_add_value_string(lbaf, "attribute_name", (print_ext_smart_id(VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB) + 7));
+
+ json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_LIFETIME_READS_TO_HOST_MSB << 8 | VS_ATTR_ID_LIFETIME_READS_TO_HOST_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbLifRdToHost, (uint64_t)lsbLifRdToHost);
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(lbafs, lbaf);
+
+
+ lbaf = json_create_object();
+
+ json_object_add_value_string(lbaf, "attribute_name", (print_ext_smart_id(VS_ATTR_ID_TRIM_COUNT_LSB) + 7));
+
+ json_object_add_value_int(lbaf, "attribute_id", VS_ATTR_ID_TRIM_COUNT_MSB << 8 | VS_ATTR_ID_TRIM_COUNT_LSB);
+
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", (uint64_t)msbTrimCnt, (uint64_t)lsbTrimCnt);
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(lbafs, lbaf);
+}
+
+static void print_smart_log_CF(vendor_log_page_CF *pLogPageCF)
+{
+ __u64 currentTemp, maxTemp;
+ printf("\n\nSeagate DRAM Supercap SMART Attributes :\n");
+ printf("%-39s %-19s \n", "Description", "Supercap Attributes");
+
+ printf("%-40s", "Super-cap current temperature");
+ currentTemp = pLogPageCF->AttrCF.SuperCapCurrentTemperature;
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(currentTemp));
+
+ maxTemp = pLogPageCF->AttrCF.SuperCapMaximumTemperature;
+ printf("%-40s", "Super-cap maximum temperature");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(maxTemp));
+
+ printf("%-40s", "Super-cap status");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageCF->AttrCF.SuperCapStatus));
+
+ printf("%-40s", "Data units read to DRAM namespace");
+ printf(" 0x%016"PRIx64"%016"PRIx64"\n", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.MS__u64),
+ le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.LS__u64));
+
+ printf("%-40s", "Data units written to DRAM namespace");
+ printf(" 0x%016"PRIx64"%016"PRIx64"\n", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.MS__u64),
+ le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.LS__u64));
+
+ printf("%-40s", "DRAM correctable error count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageCF->AttrCF.DramCorrectableErrorCount));
+
+ printf("%-40s", "DRAM uncorrectable error count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageCF->AttrCF.DramUncorrectableErrorCount));
+}
+
+static void json_print_smart_log_CF(struct json_object *root, vendor_log_page_CF *pLogPageCF)
+{
+ struct json_object *logPages;
+ unsigned int currentTemp, maxTemp;
+ char buf[40];
+
+ logPages = json_create_array();
+ json_object_add_value_array(root, "DRAM Supercap SMART Attributes", logPages);
+ struct json_object *lbaf = json_create_object();
+
+ currentTemp = pLogPageCF->AttrCF.SuperCapCurrentTemperature;
+ json_object_add_value_string(lbaf, "attribute_name", "Super-cap current temperature");
+ json_object_add_value_int(lbaf, "attribute_value", currentTemp);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ maxTemp = pLogPageCF->AttrCF.SuperCapMaximumTemperature;
+ json_object_add_value_string(lbaf, "attribute_name", "Super-cap maximum temperature");
+ json_object_add_value_int(lbaf, "attribute_value", maxTemp);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Super-cap status");
+ json_object_add_value_int(lbaf, "attribute_value", pLogPageCF->AttrCF.SuperCapStatus);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Data units read to DRAM namespace");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.MS__u64),
+ le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.LS__u64));
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Data units written to DRAM namespace");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.MS__u64),
+ le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.LS__u64));
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "DRAM correctable error count");
+ json_object_add_value_int(lbaf, "attribute_value", pLogPageCF->AttrCF.DramCorrectableErrorCount);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "DRAM uncorrectable error count");
+ json_object_add_value_int(lbaf, "attribute_value", pLogPageCF->AttrCF.DramUncorrectableErrorCount);
+ json_array_add_value_object(logPages, lbaf);
+}
+
+
+static void print_stx_smart_log_C0(STX_EXT_SMART_LOG_PAGE_C0 *pLogPageC0)
+{
+ printf("\n\nSeagate SMART Health Attributes :\n");
+ printf("%-39s %-19s \n", "Description", "Health Attributes");
+
+ printf("%-40s", "Physical Media Units Written");
+ printf(" 0x%016"PRIx64"%016"PRIx64"\n", le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.MS__u64),
+ le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.LS__u64));
+
+ printf("%-40s", "Physical Media Units Read");
+ printf(" 0x%016"PRIx64"%016"PRIx64"\n", le64_to_cpu(pLogPageC0->phyMediaUnitsRd.MS__u64),
+ le64_to_cpu(pLogPageC0->phyMediaUnitsRd.LS__u64));
+
+ printf("%-40s", "Bad User NAND Blocks");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->badUsrNandBlocks));
+
+ printf("%-40s", "Bad System NAND Blocks");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->badSysNandBlocks));
+
+ printf("%-40s", "XOR Recovery Count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->xorRecoveryCnt));
+
+ printf("%-40s", "Uncorrectable Read Error Count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->ucRdEc));
+
+ printf("%-40s", "Soft ECC Error Count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->softEccEc));
+
+ printf("%-40s", "End to End Correction Counts");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->etoeCrrCnt));
+
+ printf("%-40s", "System Data Used in Parcent");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->sysDataUsed));
+
+ printf("%-40s", "Refresh Counts");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->refreshCount));
+
+ printf("%-40s", "User Data Erase Counts");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->usrDataEraseCnt));
+
+ printf("%-40s", "Thermal Throttling Status and Count");
+ printf(" 0x%04x\n", le16_to_cpu(pLogPageC0->thermalThrottling));
+
+ printf("%-40s", "DSSD Specification Version");
+ printf(" %d.%d.%d.%d\n", pLogPageC0->dssdSpecVerMajor,
+ le16_to_cpu(pLogPageC0->dssdSpecVerMinor),
+ le16_to_cpu(pLogPageC0->dssdSpecVerPoint),
+ pLogPageC0->dssdSpecVerErrata);
+
+ printf("%-40s", "PCIe Correctable Error Count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->pcieCorrEc));
+
+ printf("%-40s", "Incomplete Shutdowns");
+ printf(" 0x%08x\n", le32_to_cpu(pLogPageC0->incompleteShutdowns));
+
+ printf("%-40s", "Free Blocks in Percent");
+ printf(" %d\n", pLogPageC0->freeBlocks);
+
+ printf("%-40s", "Capacitor Health");
+ printf(" 0x%04x\n", le16_to_cpu(pLogPageC0->capHealth));
+
+ printf("%-40s", "NVMe Errata Version");
+ printf(" %c\n", pLogPageC0->nvmeErrataVer);
+
+ printf("%-40s", "Unaligned IO");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->unalignedIO));
+
+ printf("%-40s", "Security Version Number");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->secVerNum));
+
+ printf("%-40s", "Total Namespace Utilization");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->totalNUSE));
+
+ printf("%-40s", "PLP Start Count");
+ printf(" 0x%016"PRIx64"%016"PRIx64"\n", le64_to_cpu(pLogPageC0->plpStartCnt.MS__u64),
+ le64_to_cpu(pLogPageC0->plpStartCnt.LS__u64));
+
+ printf("%-40s", "Endurance Estimate");
+ printf(" 0x%016"PRIx64"%016"PRIx64"\n", le64_to_cpu(pLogPageC0->enduranceEstimate.MS__u64),
+ le64_to_cpu(pLogPageC0->enduranceEstimate.LS__u64));
+
+ printf("%-40s", "PCIe Link Retraining Count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->pcieLinkRetCnt));
+
+ printf("%-40s", "Power State Change Count");
+ printf(" 0x%016"PRIx64"\n", le64_to_cpu(pLogPageC0->powStateChangeCnt));
+
+ printf("%-40s", "Log Page Version");
+ printf(" 0x%04x\n", le16_to_cpu(pLogPageC0->logPageVer));
+
+ printf("%-40s", "Log Page GUID");
+ printf(" 0x%016"PRIx64"%016"PRIx64"\n", le64_to_cpu(pLogPageC0->logPageGUID.MS__u64),
+ le64_to_cpu(pLogPageC0->logPageGUID.LS__u64));
+}
+
+static void json_print_stx_smart_log_C0(struct json_object *root, STX_EXT_SMART_LOG_PAGE_C0 *pLogPageC0)
+{
+ struct json_object *logPages;
+ char buf[40];
+
+ logPages = json_create_array();
+ json_object_add_value_array(root, "Seagate SMART Health Attributes", logPages);
+
+ struct json_object *lbaf = json_create_object();
+
+ json_object_add_value_string(lbaf, "attribute_name", "Physical Media Units Written");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.MS__u64),
+ le64_to_cpu(pLogPageC0->phyMediaUnitsWrt.LS__u64));
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Physical Media Units Read");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->phyMediaUnitsRd.MS__u64),
+ le64_to_cpu(pLogPageC0->phyMediaUnitsRd.LS__u64));
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Bad User NAND Blocks");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->badUsrNandBlocks));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Bad System NAND Blocks");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->badSysNandBlocks));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "XOR Recovery Count");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->xorRecoveryCnt));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Uncorrectable Read Error Count");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->ucRdEc));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Soft ECC Error Count");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->softEccEc));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "End to End Correction Counts");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->etoeCrrCnt));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "System Data Used in Parcent");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->sysDataUsed));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Refresh Counts");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->refreshCount));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "User Data Erase Counts");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->usrDataEraseCnt));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Thermal Throttling Status and Count");
+ json_object_add_value_int(lbaf, "attribute_value", le16_to_cpu(pLogPageC0->thermalThrottling));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "DSSD Specification Version");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "%d.%d.%d.%d", pLogPageC0->dssdSpecVerMajor,
+ le16_to_cpu(pLogPageC0->dssdSpecVerMinor),
+ le16_to_cpu(pLogPageC0->dssdSpecVerPoint),
+ pLogPageC0->dssdSpecVerErrata);
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "PCIe Correctable Error Count");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->pcieCorrEc));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Incomplete Shutdowns");
+ json_object_add_value_int(lbaf, "attribute_value", le32_to_cpu(pLogPageC0->incompleteShutdowns));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Free Blocks in Percent");
+ json_object_add_value_int(lbaf, "attribute_value", pLogPageC0->freeBlocks);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Capacitor Health");
+ json_object_add_value_int(lbaf, "attribute_value", le16_to_cpu(pLogPageC0->capHealth));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "NVMe Errata Version");
+ json_object_add_value_int(lbaf, "attribute_value", pLogPageC0->nvmeErrataVer);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Unaligned IO");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->unalignedIO));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Security Version Number");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->secVerNum));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Total Namespace Utilization");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->totalNUSE));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "PLP Start Count");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->plpStartCnt.MS__u64),
+ le64_to_cpu(pLogPageC0->plpStartCnt.LS__u64));
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Endurance Estimate");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->enduranceEstimate.MS__u64),
+ le64_to_cpu(pLogPageC0->enduranceEstimate.LS__u64));
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "PCIe Link Retraining Count");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->pcieLinkRetCnt));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Power State Change Count");
+ json_object_add_value_int(lbaf, "attribute_value", le64_to_cpu(pLogPageC0->powStateChangeCnt));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Log Page Version");
+ json_object_add_value_int(lbaf, "attribute_value", le16_to_cpu(pLogPageC0->logPageVer));
+ json_array_add_value_object(logPages, lbaf);
+
+ lbaf = json_create_object();
+ json_object_add_value_string(lbaf, "attribute_name", "Log Page GUID");
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageC0->logPageGUID.MS__u64),
+ le64_to_cpu(pLogPageC0->logPageGUID.LS__u64));
+ json_object_add_value_string(lbaf, "attribute_value", buf);
+ json_array_add_value_object(logPages, lbaf);
+}
+
+static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_id_ctrl ctrl;
+ char modelNo[40];
+ STX_EXT_SMART_LOG_PAGE_C0 ehExtSmart;
+ EXTENDED_SMART_INFO_T ExtdSMARTInfo;
+ vendor_log_page_CF logPageCF;
+ struct json_object *root = json_create_object();
+ struct json_object *lbafs = json_create_array();
+ struct json_object *lbafs_ExtSmart, *lbafs_DramSmart;
+
+ const char *desc = "Retrieve the Firmware Activation History for Seagate NVMe drives";
+ const char *output_format = "output in binary format";
+ struct nvme_dev *dev;
+ int err, index = 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),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf ("\nDevice not found \n");
+ return -1;
+ }
+
+ if (strcmp(cfg.output_format, "json"))
+ printf("Seagate Extended SMART Information :\n");
+
+
+ /**
+ * Here we should identify if the drive is a Panthor or Jaguar.
+ * Here we need to extract the model no from ctrl-id abd use it
+ * to deternine drive family.
+ */
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (!err) {
+ memcpy(modelNo, ctrl.mn, sizeof(modelNo));
+ } else {
+ nvme_show_status(err);
+ return err;
+ }
+
+ if (stx_is_jag_pan(modelNo) == 0) {
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xC4, sizeof(ExtdSMARTInfo), &ExtdSMARTInfo);
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ printf("%-39s %-15s %-19s \n", "Description", "Ext-Smart-Id", "Ext-Smart-Value");
+ for (index = 0; index < 80; index++)
+ printf("-");
+ printf("\n");
+ for (index = 0; index < NUMBER_EXTENDED_SMART_ATTRIBUTES; index++)
+ print_smart_log(ExtdSMARTInfo.Version, ExtdSMARTInfo.vendorData[index], index == (NUMBER_EXTENDED_SMART_ATTRIBUTES - 1));
+
+ } else {
+ lbafs_ExtSmart = json_create_object();
+ json_print_smart_log(lbafs_ExtSmart, &ExtdSMARTInfo);
+
+ json_object_add_value_array(root, "SMART-Attributes", lbafs);
+ json_array_add_value_object(lbafs, lbafs_ExtSmart);
+ }
+
+ /**
+ * Next get Log Page 0xCF
+ */
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xCF, sizeof(logPageCF), &logPageCF);
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ print_smart_log_CF(&logPageCF);
+ } else {
+ lbafs_DramSmart = json_create_object();
+ json_print_smart_log_CF(lbafs_DramSmart, &logPageCF);
+ json_array_add_value_object(lbafs, lbafs_DramSmart);
+ json_print_object(root, NULL);
+ }
+ } else if (!strcmp(cfg.output_format, "json")) {
+ json_print_object(root, NULL);
+ json_free_object(root);
+ }
+ } else if (err > 0)
+ nvme_show_status(err);
+ } else {
+ err = nvme_get_log_simple(dev_fd(dev), 0xC0, sizeof(ehExtSmart), &ehExtSmart);
+
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ print_stx_smart_log_C0(&ehExtSmart);
+
+ } else {
+ lbafs_ExtSmart = json_create_object();
+ json_print_stx_smart_log_C0(lbafs_ExtSmart, &ehExtSmart);
+
+ json_object_add_value_array(root, "SMART-Attributes", lbafs);
+ json_array_add_value_object(lbafs, lbafs_ExtSmart);
+
+ json_print_object(root, NULL);
+ json_free_object(root);
+ }
+ }
+
+ if (err > 0)
+ nvme_show_status(err);
+ }
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xC4,
+ sizeof(ExtdSMARTInfo), &ExtdSMARTInfo);
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ printf("%-39s %-15s %-19s \n", "Description", "Ext-Smart-Id", "Ext-Smart-Value");
+ for (index = 0; index < 80; index++)
+ printf("-");
+ printf("\n");
+ for (index = 0; index < NUMBER_EXTENDED_SMART_ATTRIBUTES; index++)
+ print_smart_log(ExtdSMARTInfo.Version, ExtdSMARTInfo.vendorData[index], index == (NUMBER_EXTENDED_SMART_ATTRIBUTES - 1));
+
+ } else {
+ lbafs_ExtSmart = json_create_object();
+ json_print_smart_log(lbafs_ExtSmart, &ExtdSMARTInfo);
+
+ json_object_add_value_array(root, "SMART-Attributes", lbafs);
+ json_array_add_value_object(lbafs, lbafs_ExtSmart);
+ }
+
+ /**
+ * Next get Log Page 0xCF
+ */
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xCF,
+ sizeof(logPageCF), &logPageCF);
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ print_smart_log_CF(&logPageCF);
+ } else {
+ lbafs_DramSmart = json_create_object();
+ json_print_smart_log_CF(lbafs_DramSmart, &logPageCF);
+ json_array_add_value_object(lbafs, lbafs_DramSmart);
+ json_print_object(root, NULL);
+ }
+ } else if (!strcmp(cfg.output_format, "json"))
+ json_print_object(root, NULL);
+ } else if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+
+ return err;
+}
+
+/*EOF Extended-SMART Information */
+
+/***************************************
+ * Temperature-Stats information
+ ***************************************/
+static void json_temp_stats(__u32 temperature, __u32 PcbTemp, __u32 SocTemp, __u32 maxTemperature,
+ __u32 MaxSocTemp, __u32 cf_err, __u32 scCurrentTemp, __u32 scMaxTem)
+{
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Current temperature", temperature);
+ json_object_add_value_int(root, "Current PCB temperature", PcbTemp);
+ json_object_add_value_int(root, "Current SOC temperature", SocTemp);
+ json_object_add_value_int(root, "Highest temperature", maxTemperature);
+ json_object_add_value_int(root, "Max SOC temperature", MaxSocTemp);
+ if (!cf_err) {
+ json_object_add_value_int(root, "SuperCap Current temperature", scCurrentTemp);
+ json_object_add_value_int(root, "SuperCap Max temperature", scMaxTem);
+ }
+
+ json_print_object(root, NULL);
+}
+
+static int temp_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_smart_log smart_log;
+ EXTENDED_SMART_INFO_T ExtdSMARTInfo;
+ vendor_log_page_CF logPageCF;
+
+ int err, cf_err;
+ int index;
+ const char *desc = "Retrieve Seagate Temperature Stats information for the given device ";
+ const char *output_format = "output in binary format";
+ unsigned int temperature = 0, PcbTemp = 0, SocTemp = 0, scCurrentTemp = 0, scMaxTemp = 0;
+ unsigned long long maxTemperature = 0, MaxSocTemp = 0;
+ struct nvme_dev *dev;
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf ("\nDevice not found \n");;
+ return -1;
+ }
+
+ if (strcmp(cfg.output_format, "json"))
+ printf("Seagate Temperature Stats Information :\n");
+ /*STEP-1 : Get Current Temperature from SMART */
+ err = nvme_get_log_smart(dev_fd(dev), 0xffffffff, false, &smart_log);
+ if (!err) {
+ temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]);
+ temperature = temperature ? temperature - 273 : 0;
+ PcbTemp = le16_to_cpu(smart_log.temp_sensor[0]);
+ PcbTemp = PcbTemp ? PcbTemp - 273 : 0;
+ SocTemp = le16_to_cpu(smart_log.temp_sensor[1]);
+ SocTemp = SocTemp ? SocTemp - 273 : 0;
+ if (strcmp(cfg.output_format, "json")) {
+ printf("%-20s : %u C\n", "Current Temperature", temperature);
+ printf("%-20s : %u C\n", "Current PCB Temperature", PcbTemp);
+ printf("%-20s : %u C\n", "Current SOC Temperature", SocTemp);
+ }
+ }
+
+ /* STEP-2 : Get Max temperature form Ext SMART-id 194 */
+ err = nvme_get_log_simple(dev_fd(dev), 0xC4,
+ 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) {
+ maxTemperature = smart_attribute_vs(ExtdSMARTInfo.Version, ExtdSMARTInfo.vendorData[index]);
+ maxTemperature = maxTemperature ? maxTemperature - 273 : 0;
+ if (strcmp(cfg.output_format, "json"))
+ printf("%-20s : %d C\n", "Highest Temperature", (unsigned int)maxTemperature);
+ }
+
+ if (ExtdSMARTInfo.vendorData[index].AttributeNumber == VS_ATTR_ID_MAX_SOC_LIFE_TEMPERATURE) {
+ MaxSocTemp = smart_attribute_vs(ExtdSMARTInfo.Version, ExtdSMARTInfo.vendorData[index]);
+ MaxSocTemp = MaxSocTemp ? MaxSocTemp - 273 : 0;
+ if (strcmp(cfg.output_format, "json"))
+ printf("%-20s : %d C\n", "Max SOC Temperature", (unsigned int)MaxSocTemp);
+ }
+ }
+ } else {
+ if (err > 0)
+ nvme_show_status(err);
+ }
+
+ cf_err = nvme_get_log_simple(dev_fd(dev), 0xCF,
+ sizeof(ExtdSMARTInfo), &logPageCF);
+
+ if (!cf_err) {
+ scCurrentTemp = logPageCF.AttrCF.SuperCapCurrentTemperature;
+ scCurrentTemp = scCurrentTemp ? scCurrentTemp - 273 : 0;
+ printf("%-20s : %d C\n", "Super-cap Current Temperature", scCurrentTemp);
+
+ scMaxTemp = logPageCF.AttrCF.SuperCapMaximumTemperature;
+ scMaxTemp = scMaxTemp ? scMaxTemp - 273 : 0;
+ printf("%-20s : %d C\n", "Super-cap Max Temperature", scMaxTemp);
+ }
+
+ if (!strcmp(cfg.output_format, "json"))
+ json_temp_stats(temperature, PcbTemp, SocTemp, maxTemperature, MaxSocTemp, cf_err, scCurrentTemp, scMaxTemp);
+
+ dev_close(dev);
+ return err;
+}
+/* EOF Temperature Stats information */
+
+/***************************************
+ * PCIe error-log information
+ ***************************************/
+static void print_vs_pcie_error_log(pcie_error_log_page pcieErrorLog)
+{
+ __u32 correctPcieEc = 0;
+ __u32 uncorrectPcieEc = 0;
+ correctPcieEc = pcieErrorLog.BadDllpErrCnt + pcieErrorLog.BadTlpErrCnt
+ + pcieErrorLog.RcvrErrCnt + pcieErrorLog.ReplayTOErrCnt
+ + pcieErrorLog.ReplayNumRolloverErrCnt;
+
+ uncorrectPcieEc = pcieErrorLog.FCProtocolErrCnt + pcieErrorLog.DllpProtocolErrCnt
+ + pcieErrorLog.CmpltnTOErrCnt + pcieErrorLog.RcvrQOverflowErrCnt
+ + pcieErrorLog.UnexpectedCplTlpErrCnt + pcieErrorLog.CplTlpURErrCnt
+ + pcieErrorLog.CplTlpCAErrCnt + pcieErrorLog.ReqCAErrCnt
+ + pcieErrorLog.ReqURErrCnt + pcieErrorLog.EcrcErrCnt
+ + pcieErrorLog.MalformedTlpErrCnt + pcieErrorLog.CplTlpPoisonedErrCnt
+ + pcieErrorLog.MemRdTlpPoisonedErrCnt;
+
+ printf("%-45s : %u\n", "PCIe Correctable Error Count", correctPcieEc);
+ printf("%-45s : %u\n", "PCIe Un-Correctable Error Count", uncorrectPcieEc);
+ printf("%-45s : %u\n", "Unsupported Request Error Status (URES)", pcieErrorLog.ReqURErrCnt);
+ printf("%-45s : %u\n", "ECRC Error Status (ECRCES)", pcieErrorLog.EcrcErrCnt);
+ printf("%-45s : %u\n", "Malformed TLP Status (MTS)", pcieErrorLog.MalformedTlpErrCnt);
+ printf("%-45s : %u\n", "Receiver Overflow Status (ROS)", pcieErrorLog.RcvrQOverflowErrCnt);
+ printf("%-45s : %u\n", "Unexpected Completion Status(UCS)", pcieErrorLog.UnexpectedCplTlpErrCnt);
+ printf("%-45s : %u\n", "Completion Timeout Status (CTS)", pcieErrorLog.CmpltnTOErrCnt);
+ printf("%-45s : %u\n", "Flow Control Protocol Error Status (FCPES)", pcieErrorLog.FCProtocolErrCnt);
+ printf("%-45s : %u\n", "Poisoned TLP Status (PTS)", pcieErrorLog.MemRdTlpPoisonedErrCnt);
+ printf("%-45s : %u\n", "Data Link Protocol Error Status(DLPES)", pcieErrorLog.DllpProtocolErrCnt);
+ printf("%-45s : %u\n", "Replay Timer Timeout Status(RTS)", pcieErrorLog.ReplayTOErrCnt);
+ printf("%-45s : %u\n", "Replay_NUM Rollover Status(RRS)", pcieErrorLog.ReplayNumRolloverErrCnt);
+ printf("%-45s : %u\n", "Bad DLLP Status (BDS)", pcieErrorLog.BadDllpErrCnt);
+ printf("%-45s : %u\n", "Bad TLP Status (BTS)", pcieErrorLog.BadTlpErrCnt);
+ printf("%-45s : %u\n", "Receiver Error Status (RES)", pcieErrorLog.RcvrErrCnt);
+ printf("%-45s : %u\n", "Cpl TLP Unsupported Request Error Count", pcieErrorLog.CplTlpURErrCnt);
+ printf("%-45s : %u\n", "Cpl TLP Completion Abort Error Count", pcieErrorLog.CplTlpCAErrCnt);
+ printf("%-45s : %u\n", "Cpl TLP Poisoned Error Count", pcieErrorLog.CplTlpPoisonedErrCnt);
+ printf("%-45s : %u\n", "Request Completion Abort Error Count", pcieErrorLog.ReqCAErrCnt);
+ printf("%-45s : %s\n", "Advisory Non-Fatal Error Status(ANFES)", "Not Supported");
+ printf("%-45s : %s\n", "Completer Abort Status (CAS)", "Not Supported");
+}
+
+static void json_vs_pcie_error_log(pcie_error_log_page pcieErrorLog)
+{
+ struct json_object *root;
+ root = json_create_object();
+ __u32 correctPcieEc = 0;
+ __u32 uncorrectPcieEc = 0;
+ correctPcieEc = pcieErrorLog.BadDllpErrCnt + pcieErrorLog.BadTlpErrCnt
+ + pcieErrorLog.RcvrErrCnt + pcieErrorLog.ReplayTOErrCnt
+ + pcieErrorLog.ReplayNumRolloverErrCnt;
+
+ uncorrectPcieEc = pcieErrorLog.FCProtocolErrCnt + pcieErrorLog.DllpProtocolErrCnt
+ + pcieErrorLog.CmpltnTOErrCnt + pcieErrorLog.RcvrQOverflowErrCnt
+ + pcieErrorLog.UnexpectedCplTlpErrCnt + pcieErrorLog.CplTlpURErrCnt
+ + pcieErrorLog.CplTlpCAErrCnt + pcieErrorLog.ReqCAErrCnt
+ + pcieErrorLog.ReqURErrCnt + pcieErrorLog.EcrcErrCnt
+ + pcieErrorLog.MalformedTlpErrCnt + pcieErrorLog.CplTlpPoisonedErrCnt
+ + pcieErrorLog.MemRdTlpPoisonedErrCnt;
+
+ json_object_add_value_int(root, "PCIe Correctable Error Count", correctPcieEc);
+ json_object_add_value_int(root, "PCIe Un-Correctable Error Count", uncorrectPcieEc);
+ json_object_add_value_int(root, "Unsupported Request Error Status (URES)", pcieErrorLog.ReqURErrCnt);
+ json_object_add_value_int(root, "ECRC Error Status (ECRCES)", pcieErrorLog.EcrcErrCnt);
+ json_object_add_value_int(root, "Malformed TLP Status (MTS)", pcieErrorLog.MalformedTlpErrCnt);
+ json_object_add_value_int(root, "Receiver Overflow Status (ROS)", pcieErrorLog.RcvrQOverflowErrCnt);
+ json_object_add_value_int(root, "Unexpected Completion Status(UCS)", pcieErrorLog.UnexpectedCplTlpErrCnt);
+ json_object_add_value_int(root, "Completion Timeout Status (CTS)", pcieErrorLog.CmpltnTOErrCnt);
+ json_object_add_value_int(root, "Flow Control Protocol Error Status (FCPES)", pcieErrorLog.FCProtocolErrCnt);
+ json_object_add_value_int(root, "Poisoned TLP Status (PTS)", pcieErrorLog.MemRdTlpPoisonedErrCnt);
+ json_object_add_value_int(root, "Data Link Protocol Error Status(DLPES)", pcieErrorLog.DllpProtocolErrCnt);
+ json_object_add_value_int(root, "Replay Timer Timeout Status(RTS)", pcieErrorLog.ReplayTOErrCnt);
+ json_object_add_value_int(root, "Replay_NUM Rollover Status(RRS)", pcieErrorLog.ReplayNumRolloverErrCnt);
+ json_object_add_value_int(root, "Bad DLLP Status (BDS)", pcieErrorLog.BadDllpErrCnt);
+ json_object_add_value_int(root, "Bad TLP Status (BTS)", pcieErrorLog.BadTlpErrCnt);
+ json_object_add_value_int(root, "Receiver Error Status (RES)", pcieErrorLog.RcvrErrCnt);
+ json_object_add_value_int(root, "Cpl TLP Unsupported Request Error Count", pcieErrorLog.CplTlpURErrCnt);
+ json_object_add_value_int(root, "Cpl TLP Completion Abort Error Count", pcieErrorLog.CplTlpCAErrCnt);
+ json_object_add_value_int(root, "Cpl TLP Poisoned Error Count", pcieErrorLog.CplTlpPoisonedErrCnt);
+ json_object_add_value_int(root, "Request Completion Abort Error Count", pcieErrorLog.ReqCAErrCnt);
+ json_print_object(root, NULL);
+}
+
+static int vs_pcie_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ pcie_error_log_page pcieErrorLog;
+ struct nvme_dev *dev;
+
+ const char *desc = "Retrieve Seagate PCIe error counters for the given device ";
+ const char *output_format = "output in binary format";
+ int err;
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf ("\nDevice not found \n");;
+ return -1;
+ }
+
+ if (strcmp(cfg.output_format, "json"))
+ printf("Seagate PCIe error counters Information :\n");
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xCB,
+ sizeof(pcieErrorLog), &pcieErrorLog);
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ print_vs_pcie_error_log(pcieErrorLog);
+ } else
+ json_vs_pcie_error_log(pcieErrorLog);
+
+ } else if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+/* EOF PCIE error-log information */
+
+
+/***************************************
+ * FW Activation History log
+ ***************************************/
+static void print_stx_vs_fw_activate_history(stx_fw_activ_history_log_page fwActivHis)
+{
+ __u32 i;
+ char prev_fw[9] = {0};
+ char new_fw[9] = {0};
+ char buf[80];
+
+ if (fwActivHis.numValidFwActHisEnt > 0) {
+ printf("\n\nSeagate FW Activation Histry :\n");
+ printf("%-9s %-21s %-7s %-13s %-9s %-5s %-15s %-9s\n", "Counter ", " Timestamp ", " PCC ", "Previous FW ", "New FW ", "Slot", "Commit Action", "Result");
+
+ for (i = 0; i < fwActivHis.numValidFwActHisEnt; i++) {
+
+ printf(" %-4d ", fwActivHis.fwActHisEnt[i].fwActivCnt);
+
+ time_t t = fwActivHis.fwActHisEnt[i].timeStamp / 1000;
+ struct tm ts;
+ ts = *localtime(&t);
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &ts);
+ printf(" %-20s ", buf);
+ printf("%-5" PRId64 " ",
+ (uint64_t)fwActivHis.fwActHisEnt[i].powCycleCnt);
+
+ memset(prev_fw, 0, sizeof(prev_fw));
+ memcpy(prev_fw, fwActivHis.fwActHisEnt[i].previousFW, sizeof(fwActivHis.fwActHisEnt[i].previousFW));
+ printf("%-8s ", prev_fw);
+
+ memset(new_fw, 0, sizeof(new_fw));
+ memcpy(new_fw, fwActivHis.fwActHisEnt[i].newFW, sizeof(fwActivHis.fwActHisEnt[i].newFW));
+ printf("%-8s ", new_fw);
+
+ printf(" %-2d ", fwActivHis.fwActHisEnt[i].slotNum);
+ printf(" 0x%02x ", fwActivHis.fwActHisEnt[i].commitActionType);
+ printf(" 0x%02x \n", fwActivHis.fwActHisEnt[i].result);
+ }
+ } else {
+ printf("%s\n", "Do not have valid FW Activation History");
+ }
+}
+
+static void json_stx_vs_fw_activate_history(stx_fw_activ_history_log_page fwActivHis)
+{
+ struct json_object *root;
+ root = json_create_object();
+ __u32 i;
+
+ char buf[80];
+
+ struct json_object *historyLogPage;
+ historyLogPage = json_create_array();
+ json_object_add_value_array(root, "Seagate FW Activation History", historyLogPage);
+
+ if (fwActivHis.numValidFwActHisEnt > 0) {
+ for (i = 0; i < fwActivHis.numValidFwActHisEnt; i++) {
+ struct json_object *lbaf = json_create_object();
+ char prev_fw[8] = { 0 };
+ char new_fw[8] = { 0 };
+
+ json_object_add_value_int(lbaf, "Counter", fwActivHis.fwActHisEnt[i].fwActivCnt);
+
+ time_t t = fwActivHis.fwActHisEnt[i].timeStamp / 1000;
+ struct tm ts;
+ ts = *localtime(&t);
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &ts);
+ printf(" %-20s ", buf);
+ json_object_add_value_string(lbaf, "Timestamp", buf);
+
+ json_object_add_value_int(lbaf, "PCC", fwActivHis.fwActHisEnt[i].powCycleCnt);
+ sprintf(prev_fw, "%s", fwActivHis.fwActHisEnt[i].previousFW);
+ json_object_add_value_string(lbaf, "Previous_FW", prev_fw);
+
+ sprintf(new_fw, "%s", fwActivHis.fwActHisEnt[i].newFW);
+ json_object_add_value_string(lbaf, "New_FW", new_fw);
+
+ json_object_add_value_int(lbaf, "Slot", fwActivHis.fwActHisEnt[i].slotNum);
+ json_object_add_value_int(lbaf, "Commit_Action", fwActivHis.fwActHisEnt[i].commitActionType);
+ json_object_add_value_int(lbaf, "Result", fwActivHis.fwActHisEnt[i].result);
+
+ json_array_add_value_object(historyLogPage, lbaf);
+ }
+ } else {
+ printf("%s\n", "Do not have valid FW Activation History");
+ }
+
+ json_print_object(root, NULL);
+ json_free_object(root);
+}
+
+static int stx_vs_fw_activate_history(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ stx_fw_activ_history_log_page fwActivHis;
+ struct nvme_dev *dev;
+
+ const char *desc = "Retrieve FW Activate History for Seagate device ";
+ const char *output_format = "output in binary format";
+ int err;
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err < 0) {
+ printf ("\nDevice not found \n");;
+ return -1;
+ }
+
+ if (strcmp(cfg.output_format, "json"))
+ printf("Seagate FW Activation Histry Information :\n");
+
+ err = nvme_get_log_simple(dev_fd(dev), 0xC2, sizeof(fwActivHis), &fwActivHis);
+ if (!err) {
+ if (strcmp(cfg.output_format, "json")) {
+ print_stx_vs_fw_activate_history(fwActivHis);
+ } else
+ json_stx_vs_fw_activate_history(fwActivHis);
+
+ } else if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+/* EOF FW Activation History log information */
+
+
+static int clear_fw_activate_history(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Clear FW Activation History for the given Seagate device ";
+ const char *save = "specifies that the controller shall save the attribute";
+ int err;
+ struct nvme_dev *dev;
+ struct nvme_id_ctrl ctrl;
+ char modelNo[40];
+ __u32 result;
+
+ struct config {
+ bool save;
+ };
+
+ struct config cfg = {
+ .save = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("save", 's', &cfg.save, save),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err < 0) {
+ printf ("\nDevice not found \n");
+ return -1;
+ }
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (!err) {
+ memcpy(modelNo, ctrl.mn, sizeof(modelNo));
+ } else {
+ nvme_show_status(err);
+ return err;
+ }
+
+ if (stx_is_jag_pan(modelNo) == 0) {
+ printf ("\nDevice does not support Clear FW Activation History \n");
+ } else {
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = 0xC1,
+ .nsid = 0,
+ .cdw11 = 0x80000000,
+ .cdw12 = 0,
+ .save = 0,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ if (err)
+ fprintf(stderr, "%s: couldn't clear PCIe correctable errors \n",
+ __func__);
+ }
+
+ if (err < 0) {
+ perror("set-feature");
+ return errno;
+ }
+
+ dev_close(dev);
+ return err;
+}
+
+
+static int vs_clr_pcie_correctable_errs(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Clear Seagate PCIe Correctable counters for the given device ";
+ const char *save = "specifies that the controller shall save the attribute";
+
+ struct nvme_id_ctrl ctrl;
+ char modelNo[40];
+
+ struct nvme_dev *dev;
+
+ __u32 result;
+ int err;
+
+ struct config {
+ bool save;
+ };
+
+ struct config cfg = {
+ .save = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("save", 's', &cfg.save, save),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf ("\nDevice not found \n");;
+ return -1;
+ }
+
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (!err) {
+ memcpy(modelNo, ctrl.mn, sizeof(modelNo));
+ } else {
+ nvme_show_status(err);
+ return err;
+ }
+
+ if (stx_is_jag_pan(modelNo) == 0) {
+ err = nvme_set_features_simple(dev_fd(dev), 0xE1, 0, 0xCB, cfg.save, &result);
+ } else {
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = 0xC3,
+ .nsid = 0,
+ .cdw11 = 0x80000000,
+ .cdw12 = 0,
+ .save = 0,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ if (err)
+ fprintf(stderr, "%s: couldn't clear PCIe correctable errors \n", __func__);
+ }
+
+ err = nvme_set_features_simple(dev_fd(dev), 0xE1, 0, 0xCB, cfg.save, &result);
+
+ if (err < 0) {
+ perror("set-feature");
+ return errno;
+ }
+
+ dev_close(dev);
+ return err;
+}
+
+static int get_host_tele(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Capture the Telemetry Host-Initiated Data in either " \
+ "hex-dump (default) or binary format";
+ const char *namespace_id = "desired namespace";
+ const char *log_specific = "1 - controller shall capture Data representing the internal " \
+ "state of the controller at the time the command is processed. " \
+ "0 - controller shall not update the Telemetry Host Initiated Data.";
+ const char *raw = "output in raw format";
+ struct nvme_temetry_log_hdr tele_log;
+ int blkCnt, maxBlk = 0, blksToGet;
+ struct nvme_dev *dev;
+ unsigned char *log;
+ __le64 offset = 0;
+ int err, dump_fd;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 log_id;
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ .namespace_id = 0xffffffff,
+ .log_id = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_UINT("log_specific", 'i', &cfg.log_id, log_specific),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ dump_fd = STDOUT_FILENO;
+ cfg.log_id = (cfg.log_id << 8) | 0x07;
+ err = nvme_get_nsid_log(dev_fd(dev), false, cfg.log_id,
+ cfg.namespace_id,
+ sizeof(tele_log), (void *)(&tele_log));
+ if (!err) {
+ maxBlk = tele_log.tele_data_area3;
+ offset += 512;
+
+ if (!cfg.raw_binary) {
+ printf("Device:%s log-id:%d namespace-id:%#x\n",
+ dev->name, cfg.log_id,
+ cfg.namespace_id);
+ printf("Data Block 1 Last Block:%d Data Block 2 Last Block:%d Data Block 3 Last Block:%d\n",
+ tele_log.tele_data_area1, tele_log.tele_data_area2, tele_log.tele_data_area3);
+
+ d((unsigned char *)(&tele_log), sizeof(tele_log), 16, 1);
+ } else
+ seaget_d_raw((unsigned char *)(&tele_log), sizeof(tele_log), dump_fd);
+ } else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("log page");
+
+ blkCnt = 0;
+
+ while (blkCnt < maxBlk) {
+ unsigned long long bytesToGet;
+
+ blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt);
+
+ if (blksToGet == 0) {
+ dev_close(dev);
+ return err;
+ }
+
+ bytesToGet = (unsigned long long)blksToGet * 512;
+ log = malloc(bytesToGet);
+
+ if (!log) {
+ fprintf(stderr, "could not alloc buffer for log\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ memset(log, 0, bytesToGet);
+
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = cfg.log_id,
+ .nsid = cfg.namespace_id,
+ .lpo = offset,
+ .lsp = 0,
+ .lsi = 0,
+ .rae = true,
+ .uuidx = 0,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = bytesToGet,
+ .log = (void *)log,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ err = nvme_get_log(&args);
+ if (!err) {
+ offset += (__le64)bytesToGet;
+
+ if (!cfg.raw_binary) {
+ printf("\nBlock # :%d to %d\n", blkCnt + 1, blkCnt + blksToGet);
+
+ d((unsigned char *)log, bytesToGet, 16, 1);
+ } else
+ seaget_d_raw((unsigned char *)log, bytesToGet, dump_fd);
+ } else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("log page");
+
+ blkCnt += blksToGet;
+
+ free(log);
+ }
+
+ dev_close(dev);
+ return err;
+}
+
+static int get_ctrl_tele(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Capture the Telemetry Controller-Initiated Data in either " \
+ "hex-dump (default) or binary format";
+ const char *namespace_id = "desired namespace";
+ const char *raw = "output in raw format";
+ struct nvme_dev *dev;
+ int err, dump_fd;
+ struct nvme_temetry_log_hdr tele_log;
+ __le64 offset = 0;
+ __u16 log_id;
+ int blkCnt, maxBlk = 0, blksToGet;
+ unsigned char *log;
+
+ struct config {
+ __u32 namespace_id;
+ bool raw_binary;
+ };
+
+ struct config cfg = {
+ .namespace_id = 0xffffffff,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ dump_fd = STDOUT_FILENO;
+
+ log_id = 0x08;
+ err = nvme_get_nsid_log(dev_fd(dev), false, log_id, cfg.namespace_id,
+ sizeof(tele_log), (void *)(&tele_log));
+ if (!err) {
+ maxBlk = tele_log.tele_data_area3;
+ offset += 512;
+
+ if (!cfg.raw_binary) {
+ printf("Device:%s namespace-id:%#x\n",
+ dev->name, cfg.namespace_id);
+ printf("Data Block 1 Last Block:%d Data Block 2 Last Block:%d Data Block 3 Last Block:%d\n",
+ tele_log.tele_data_area1, tele_log.tele_data_area2, tele_log.tele_data_area3);
+
+ d((unsigned char *)(&tele_log), sizeof(tele_log), 16, 1);
+ } else
+ seaget_d_raw((unsigned char *)(&tele_log), sizeof(tele_log), dump_fd);
+ } else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("log page");
+
+ blkCnt = 0;
+
+ while (blkCnt < maxBlk) {
+ unsigned long long bytesToGet;
+
+ blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt);
+
+ if (blksToGet == 0)
+ return err;
+
+ bytesToGet = (unsigned long long)blksToGet * 512;
+ log = malloc(bytesToGet);
+
+ if (!log) {
+ fprintf(stderr, "could not alloc buffer for log\n");
+ return EINVAL;
+ }
+
+ memset(log, 0, bytesToGet);
+
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = log_id,
+ .nsid = cfg.namespace_id,
+ .lpo = offset,
+ .lsp = 0,
+ .lsi = 0,
+ .rae = true,
+ .uuidx = 0,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = bytesToGet,
+ .log = (void *)log,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ err = nvme_get_log(&args);
+ if (!err) {
+ offset += (__le64)bytesToGet;
+
+ if (!cfg.raw_binary) {
+ printf("\nBlock # :%d to %d\n", blkCnt + 1, blkCnt + blksToGet);
+
+ d((unsigned char *)log, bytesToGet, 16, 1);
+ } else
+ seaget_d_raw((unsigned char *)log, bytesToGet, dump_fd);
+ } else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("log page");
+
+ blkCnt += blksToGet;
+
+ free(log);
+ }
+
+ dev_close(dev);
+ return err;
+}
+
+void seaget_d_raw(unsigned char *buf, int len, int fd)
+{
+ if (write(fd, (void *)buf, len) <= 0)
+ printf("%s: Write Failed\n", __FUNCTION__);
+}
+
+
+static int vs_internal_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Capture the Telemetry Controller-Initiated Data in " \
+ "binary format";
+ const char *namespace_id = "desired namespace";
+
+ const char *file = "dump file";
+ struct nvme_dev *dev;
+ int err, dump_fd;
+ int flags = O_WRONLY | O_CREAT;
+ int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
+ struct nvme_temetry_log_hdr tele_log;
+ __le64 offset = 0;
+ __u16 log_id;
+ int blkCnt, maxBlk = 0, blksToGet;
+ unsigned char *log;
+
+ struct config {
+ __u32 namespace_id;
+ char *file;
+ };
+
+ struct config cfg = {
+ .namespace_id = 0xffffffff,
+ .file = "",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_FILE("dump-file", 'f', &cfg.file, file),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ dump_fd = STDOUT_FILENO;
+ if (strlen(cfg.file)) {
+ dump_fd = open(cfg.file, flags, mode);
+ if (dump_fd < 0) {
+ perror(cfg.file);
+ dev_close(dev);
+ return EINVAL;
+ }
+ }
+
+ log_id = 0x08;
+ err = nvme_get_nsid_log(dev_fd(dev), false, log_id, cfg.namespace_id,
+ sizeof(tele_log), (void *)(&tele_log));
+ if (!err) {
+ maxBlk = tele_log.tele_data_area3;
+ offset += 512;
+
+ seaget_d_raw((unsigned char *)(&tele_log), sizeof(tele_log), dump_fd);
+ } else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("log page");
+
+ blkCnt = 0;
+
+ while (blkCnt < maxBlk) {
+ unsigned long long bytesToGet;
+
+ blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt);
+
+ if (blksToGet == 0) {
+ goto out;
+ }
+
+ bytesToGet = (unsigned long long)blksToGet * 512;
+ log = malloc(bytesToGet);
+
+ if (!log) {
+ fprintf(stderr, "could not alloc buffer for log\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ memset(log, 0, bytesToGet);
+
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = log_id,
+ .nsid = cfg.namespace_id,
+ .lpo = offset,
+ .lsp = 0,
+ .lsi = 0,
+ .rae = true,
+ .uuidx = 0,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = bytesToGet,
+ .log = (void *)log,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ err = nvme_get_log(&args);
+ if (!err) {
+ offset += (__le64)bytesToGet;
+
+ seaget_d_raw((unsigned char *)log, bytesToGet, dump_fd);
+
+ } else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror("log page");
+
+ blkCnt += blksToGet;
+
+ free(log);
+ }
+out:
+ if (strlen(cfg.file))
+ close(dump_fd);
+
+ dev_close(dev);
+ return err;
+}
+
+/*SEAGATE-PLUGIN Version */
+static int seagate_plugin_version(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ printf("Seagate-Plugin version : %d.%d \n",
+ SEAGATE_PLUGIN_VERSION_MAJOR,
+ SEAGATE_PLUGIN_VERSION_MINOR);
+ return 0;
+}
+/*EOF SEAGATE-PLUGIN Version */
+
+/*OCP SEAGATE-PLUGIN Version */
+static int stx_ocp_plugin_version(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ printf("Seagate-OCP-Plugin version : %d.%d \n",
+ SEAGATE_OCP_PLUGIN_VERSION_MAJOR,
+ SEAGATE_OCP_PLUGIN_VERSION_MINOR);
+ return 0;
+}
+/*EOF OCP SEAGATE-PLUGIN Version */
diff --git a/plugins/seagate/seagate-nvme.h b/plugins/seagate/seagate-nvme.h
new file mode 100644
index 0000000..99f6327
--- /dev/null
+++ b/plugins/seagate/seagate-nvme.h
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Do NOT modify or remove this copyright and license
+ *
+ * Copyright (c) 2017-2018 Seagate Technology LLC and/or its Affiliates, All Rights Reserved
+ *
+ * ******************************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * \file seagate-nvme.h
+ * \brief This file defines the functions and macros to make building a nvme-cli seagate plug-in.
+ *
+ * Author: Debabrata Bardhan <debabrata.bardhan@seagate.com>
+ */
+
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/seagate/seagate-nvme
+
+#if !defined(SEAGATE_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define SEAGATE_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("seagate", "Seagate vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("vs-temperature-stats", "Retrieve Seagate temperature statistics ", temp_stats)
+ ENTRY("vs-log-page-sup", "Retrieve Seagate Supported Log-pages Information ", log_pages_supp)
+ ENTRY("vs-smart-add-log", "Retrieve Seagate extended-SMART Information ", vs_smart_log)
+ ENTRY("vs-pcie-stats", "Retrieve Seagate PCIe error statistics ", vs_pcie_error_log)
+ ENTRY("clear-pcie-correctable-errors", "Clear Seagate PCIe error statistics ", vs_clr_pcie_correctable_errs)
+ ENTRY("get-host-tele", "Retrieve Seagate Host-Initiated Telemetry ", get_host_tele)
+ ENTRY("get-ctrl-tele", "Retrieve Seagate Controller-Initiated Telemetry ", get_ctrl_tele)
+ ENTRY("vs-internal-log", "Retrieve Seagate Controller-Initiated Telemetry in binary format", vs_internal_log)
+ ENTRY("vs-fw-activate-history", "Retrieve the Firmware Activation History", stx_vs_fw_activate_history)
+ ENTRY("clear-fw-activate-history", "Clear Firmware Activation History", clear_fw_activate_history)
+ ENTRY("plugin-version", "Shows Seagate plugin's version information ", seagate_plugin_version)
+ ENTRY("cloud-SSD-plugin-version", "Shows OCP Seagate plugin's version information ", stx_ocp_plugin_version)
+ )
+);
+
+#endif
+#include "define_cmd.h"
diff --git a/plugins/shannon/shannon-nvme.c b/plugins/shannon/shannon-nvme.c
new file mode 100644
index 0000000..424b3f7
--- /dev/null
+++ b/plugins/shannon/shannon-nvme.c
@@ -0,0 +1,404 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "shannon-nvme.h"
+
+typedef enum {
+ PROGRAM_FAIL_CNT,
+ ERASE_FAIL_CNT,
+ WEARLEVELING_COUNT,
+ E2E_ERR_CNT,
+ CRC_ERR_CNT,
+ TIME_WORKLOAD_MEDIA_WEAR,
+ TIME_WORKLOAD_HOST_READS,
+ TIME_WORKLOAD_TIMER,
+ THERMAL_THROTTLE,
+ RETRY_BUFFER_OVERFLOW,
+ PLL_LOCK_LOSS,
+ NAND_WRITE,
+ HOST_WRITE,
+ SRAM_ERROR_CNT,
+ ADD_SMART_ITEMS,
+}addtional_smart_items;
+
+#pragma pack(push,1)
+struct nvme_shannon_smart_log_item {
+ __u8 rsv1[3];
+ __u8 norm;
+ __u8 rsv2;
+ union {
+ __u8 item_val[6];
+ struct wear_level {
+ __le16 min;
+ __le16 max;
+ __le16 avg;
+ } wear_level ;
+ struct thermal_throttle {
+ __u8 st;
+ __u32 count;
+ } thermal_throttle;
+ };
+ __u8 _resv;
+};
+#pragma pack(pop)
+
+struct nvme_shannon_smart_log {
+ struct nvme_shannon_smart_log_item items[ADD_SMART_ITEMS];
+ __u8 vend_spec_resv;
+};
+
+static void show_shannon_smart_log(struct nvme_shannon_smart_log *smart,
+ unsigned int nsid, const char *devname)
+{
+ printf("Additional Smart Log for NVME device:%s namespace-id:%x\n",
+ devname, nsid);
+ printf("key normalized value\n");
+ printf("program_fail_count : %3d%% %"PRIu64"\n",
+ smart->items[PROGRAM_FAIL_CNT].norm,
+ int48_to_long(smart->items[PROGRAM_FAIL_CNT].item_val));
+ printf("erase_fail_count : %3d%% %"PRIu64"\n",
+ smart->items[ERASE_FAIL_CNT].norm,
+ int48_to_long(smart->items[ERASE_FAIL_CNT].item_val));
+ printf("wear_leveling : %3d%% min: %u, max: %u, avg: %u\n",
+ smart->items[WEARLEVELING_COUNT].norm,
+ le16_to_cpu(smart->items[WEARLEVELING_COUNT].wear_level.min),
+ le16_to_cpu(smart->items[WEARLEVELING_COUNT].wear_level.max),
+ le16_to_cpu(smart->items[WEARLEVELING_COUNT].wear_level.avg));
+ printf("end_to_end_error_detection_count: %3d%% %"PRIu64"\n",
+ smart->items[E2E_ERR_CNT].norm,
+ int48_to_long(smart->items[E2E_ERR_CNT].item_val));
+ printf("crc_error_count : %3d%% %"PRIu64"\n",
+ smart->items[CRC_ERR_CNT].norm,
+ int48_to_long(smart->items[CRC_ERR_CNT].item_val));
+ printf("timed_workload_media_wear : %3d%% %.3f%%\n",
+ smart->items[TIME_WORKLOAD_MEDIA_WEAR].norm,
+ ((float)int48_to_long(smart->items[TIME_WORKLOAD_MEDIA_WEAR].item_val)) / 1024);
+ printf("timed_workload_host_reads : %3d%% %"PRIu64"%%\n",
+ smart->items[TIME_WORKLOAD_HOST_READS].norm,
+ int48_to_long(smart->items[TIME_WORKLOAD_HOST_READS].item_val));
+ printf("timed_workload_timer : %3d%% %"PRIu64" min\n",
+ smart->items[TIME_WORKLOAD_TIMER].norm,
+ int48_to_long(smart->items[TIME_WORKLOAD_TIMER].item_val));
+ printf("thermal_throttle_status : %3d%% CurTTSta: %u%%, TTCnt: %u\n",
+ smart->items[THERMAL_THROTTLE].norm,
+ smart->items[THERMAL_THROTTLE].thermal_throttle.st,
+ smart->items[THERMAL_THROTTLE].thermal_throttle.count);
+ printf("retry_buffer_overflow_count : %3d%% %"PRIu64"\n",
+ smart->items[RETRY_BUFFER_OVERFLOW].norm,
+ int48_to_long(smart->items[RETRY_BUFFER_OVERFLOW].item_val));
+ printf("pll_lock_loss_count : %3d%% %"PRIu64"\n",
+ smart->items[PLL_LOCK_LOSS].norm,
+ int48_to_long(smart->items[PLL_LOCK_LOSS].item_val));
+ printf("nand_bytes_written : %3d%% sectors: %"PRIu64"\n",
+ smart->items[NAND_WRITE].norm,
+ int48_to_long(smart->items[NAND_WRITE].item_val));
+ printf("host_bytes_written : %3d%% sectors: %"PRIu64"\n",
+ smart->items[HOST_WRITE].norm,
+ int48_to_long(smart->items[HOST_WRITE].item_val));
+ printf("sram_error_count : %3d%% %"PRIu64"\n",
+ smart->items[RETRY_BUFFER_OVERFLOW].norm,
+ int48_to_long(smart->items[SRAM_ERROR_CNT].item_val));
+}
+
+
+static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_shannon_smart_log smart_log;
+ char *desc = "Get Shannon 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 nvme_dev *dev;
+ struct config {
+ __u32 namespace_id;
+ bool raw_binary;
+ };
+ int err;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+ err = nvme_get_nsid_log(dev_fd(dev), false, 0xca, cfg.namespace_id,
+ sizeof(smart_log), &smart_log);
+ if (!err) {
+ if (!cfg.raw_binary)
+ show_shannon_smart_log(&smart_log, cfg.namespace_id,
+ dev->name);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ else if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+static int get_additional_feature(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 "\
+ "behavior 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"\
+ "0x02: Shannon power management\n";
+ const char *raw = "show infos 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";
+ struct nvme_dev *dev;
+ void *buf = NULL;
+ __u32 result;
+ int err;
+
+ struct config {
+ __u32 namespace_id;
+ enum nvme_features_id feature_id;
+ __u8 sel;
+ __u32 cdw11;
+ __u32 data_len;
+ bool raw_binary;
+ bool human_readable;
+ };
+
+ struct config cfg = {
+ .namespace_id = 1,
+ .feature_id = 0,
+ .sel = 0,
+ .cdw11 = 0,
+ .data_len = 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_BYTE("sel", 's', &cfg.sel, sel),
+ OPT_UINT("data-len", 'l', &cfg.data_len, data_len),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_UINT("cdw11", 'c', &cfg.cdw11, cdw11),
+ OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (cfg.sel > 7) {
+ fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel);
+ dev_close(dev);
+ return EINVAL;
+ }
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+ if (cfg.data_len) {
+ if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+ {
+ dev_close(dev);
+ exit(ENOMEM);
+ }
+ memset(buf, 0, cfg.data_len);
+ }
+
+ struct nvme_get_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = cfg.feature_id,
+ .nsid = cfg.namespace_id,
+ .sel = cfg.sel,
+ .cdw11 = cfg.cdw11,
+ .uuidx = 0,
+ .data_len = cfg.data_len,
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_get_features(&args);
+ if (!err) {
+#if 0
+ printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id,
+ nvme_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);
+ }
+ }
+#endif
+ } else if (err > 0)
+ nvme_show_status(err);
+ if (buf)
+ free(buf);
+ return err;
+}
+
+static int set_additional_feature(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"\
+ "0x02: Shannon power management\n";
+ 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 ffd = STDIN_FILENO;
+ struct nvme_dev *dev;
+ void *buf = NULL;
+ __u32 result;
+ int err;
+
+ struct config {
+ char *file;
+ __u32 namespace_id;
+ __u32 feature_id;
+ __u32 value;
+ __u32 data_len;
+ bool save;
+ };
+
+ struct config cfg = {
+ .file = "",
+ .namespace_id = 0,
+ .feature_id = 0,
+ .value = 0,
+ .data_len = 0,
+ .save = 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("data-len", 'l', &cfg.data_len, data_len),
+ OPT_FILE("data", 'd', &cfg.file, data),
+ OPT_FLAG("save", 's', &cfg.save, save),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ if (cfg.data_len) {
+ if (posix_memalign(&buf, getpagesize(), cfg.data_len)){
+ fprintf(stderr, "can not allocate feature payload\n");
+ dev_close(dev);
+ return ENOMEM;
+ }
+ memset(buf, 0, cfg.data_len);
+ }
+
+ 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;
+ }
+ }
+ err = read(ffd, (void *)buf, cfg.data_len);
+ if (err < 0) {
+ fprintf(stderr, "failed to read data buffer from input file\n");
+ err = EINVAL;
+ goto free;
+ }
+ }
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = cfg.feature_id,
+ .nsid = cfg.namespace_id,
+ .cdw11 = cfg.value,
+ .cdw12 = 0,
+ .save = cfg.save,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = cfg.data_len,
+ .data = buf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ if (err < 0) {
+ perror("set-feature");
+ goto free;
+ }
+ if (!err) {
+#if 0
+ printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
+ nvme_feature_to_string(cfg.feature_id), cfg.value);
+#endif
+ if (buf)
+ d(buf, cfg.data_len, 16, 1);
+ } else if (err > 0)
+ nvme_show_status(err);
+
+free:
+ if (buf)
+ free(buf);
+ return err;
+}
+
+static int shannon_id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return __id_ctrl(argc, argv, cmd, plugin, NULL);
+}
+
+
+
diff --git a/plugins/shannon/shannon-nvme.h b/plugins/shannon/shannon-nvme.h
new file mode 100644
index 0000000..255bb6b
--- /dev/null
+++ b/plugins/shannon/shannon-nvme.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/shannon/shannon-nvme
+
+#if !defined(SHANNON_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define SHANNON_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("shannon", "Shannon vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve Shannon SMART Log, show it", get_additional_smart_log)
+ ENTRY("set-additioal-feature", "Set additional Shannon feature", set_additional_feature)
+ ENTRY("get-additional-feature", "Get additional Shannon feature", get_additional_feature)
+ ENTRY("id-ctrl", "Retrieve Shannon ctrl id, show it", shannon_id_ctrl)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/solidigm/meson.build b/plugins/solidigm/meson.build
new file mode 100644
index 0000000..bca13bb
--- /dev/null
+++ b/plugins/solidigm/meson.build
@@ -0,0 +1,7 @@
+sources += [
+ 'plugins/solidigm/solidigm-smart.c',
+ 'plugins/solidigm/solidigm-garbage-collection.c',
+ 'plugins/solidigm/solidigm-latency-tracking.c',
+ 'plugins/solidigm/solidigm-telemetry.c',
+]
+subdir('solidigm-telemetry')
diff --git a/plugins/solidigm/solidigm-garbage-collection.c b/plugins/solidigm/solidigm-garbage-collection.c
new file mode 100644
index 0000000..8e2eccc
--- /dev/null
+++ b/plugins/solidigm/solidigm-garbage-collection.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+#include "solidigm-garbage-collection.h"
+
+typedef struct __attribute__((packed)) gc_item {
+ __le32 timer_type;
+ __le64 timestamp;
+} gc_item_t;
+
+#define VU_GC_MAX_ITEMS 100
+typedef struct garbage_control_collection_log {
+ __le16 version_major;
+ __le16 version_minor;
+ gc_item_t item[VU_GC_MAX_ITEMS];
+ __u8 reserved[2892];
+} garbage_control_collection_log_t;
+
+static void vu_gc_log_show_json(garbage_control_collection_log_t *payload, const char *devname)
+{
+ struct json_object *gc_entries = json_create_array();
+
+ for (int i = 0; i < VU_GC_MAX_ITEMS; i++) {
+ gc_item_t item = payload->item[i];
+ struct json_object *entry = json_create_object();
+ json_object_add_value_int(entry, "timestamp", le64_to_cpu(item.timestamp));
+ json_object_add_value_int(entry, "timer_type", le32_to_cpu(item.timer_type));
+ json_array_add_value_object(gc_entries, entry);
+ }
+
+ json_print_object(gc_entries, NULL);
+ json_free_object(gc_entries);
+}
+
+static void vu_gc_log_show(garbage_control_collection_log_t *payload, const char *devname)
+{
+ printf("Solidigm Garbage Collection Log for NVME device: %s\n", devname);
+ printf("Timestamp Timer Type\n");
+
+ for (int i = 0; i < VU_GC_MAX_ITEMS; i++) {
+ gc_item_t item = payload->item[i];
+ printf("%-13" PRIu64 " %d\n", le64_to_cpu(item.timestamp), le32_to_cpu(item.timer_type));
+ }
+}
+
+int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Get and parse Solidigm vendor specific garbage collection event log.";
+ struct nvme_dev *dev;
+ int err;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ enum nvme_print_flags flags = validate_output_format(cfg.output_format);
+ if (flags == -EINVAL) {
+ fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format);
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ garbage_control_collection_log_t gc_log;
+ const int solidigm_vu_gc_log_id = 0xfd;
+
+ err = nvme_get_log_simple(dev_fd(dev), solidigm_vu_gc_log_id,
+ sizeof(gc_log), &gc_log);
+ if (!err) {
+ if (flags & BINARY) {
+ d_raw((unsigned char *)&gc_log, sizeof(gc_log));
+ } else if (flags & JSON) {
+ vu_gc_log_show_json(&gc_log, dev->name);
+ } else {
+ vu_gc_log_show(&gc_log, dev->name);
+ }
+ }
+ else if (err > 0) {
+ nvme_show_status(err);
+ }
+
+ /* Redundant close() to make static code analysis happy */
+ close(dev->direct.fd);
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/solidigm/solidigm-garbage-collection.h b/plugins/solidigm/solidigm-garbage-collection.h
new file mode 100644
index 0000000..a3e34b2
--- /dev/null
+++ b/plugins/solidigm/solidigm-garbage-collection.h
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+int solidigm_get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin);
diff --git a/plugins/solidigm/solidigm-latency-tracking.c b/plugins/solidigm/solidigm-latency-tracking.c
new file mode 100644
index 0000000..1013ae8
--- /dev/null
+++ b/plugins/solidigm/solidigm-latency-tracking.c
@@ -0,0 +1,475 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define BUCKET_LIST_SIZE_4_0 152
+#define BUCKET_LIST_SIZE_4_1 1216
+
+#define BASE_RANGE_BITS_4_0 3
+#define BASE_RANGE_BITS_4_1 6
+
+struct latency_statistics {
+ __u16 version_major;
+ __u16 version_minor;
+ __u32 data[BUCKET_LIST_SIZE_4_1];
+ __u64 average_latency;
+};
+
+struct config {
+ bool enable;
+ bool disable;
+ bool read;
+ bool write;
+ unsigned char type;
+ char *output_format;
+};
+
+struct latency_tracker {
+ int fd;
+ struct config cfg;
+ enum nvme_print_flags print_flags;
+ struct latency_statistics stats;
+ struct json_object *bucket_list;
+ __u32 bucket_list_size;
+ __u8 base_range_bits;
+ bool has_average_latency_field;
+};
+
+/* COL_WIDTH controls width of columns in NORMAL output. */
+#define COL_WIDTH 12
+#define BUCKET_LABEL_MAX_SIZE 10
+
+#define US_IN_S 1000000
+#define US_IN_MS 1000
+
+/*
+ * Edge buckets may have range [#s, inf) in some
+ * latency statistics formats.
+ */
+static void get_time_unit_label(char *label, __u32 microseconds,
+ bool bonded)
+{
+ char *string = "us";
+ int divisor = 1;
+
+ if (!bonded) {
+ snprintf(label, BUCKET_LABEL_MAX_SIZE, "%s", "+INF");
+ return;
+ }
+
+ if (microseconds > US_IN_S) {
+ string = "s";
+ divisor = US_IN_S;
+ } else if (microseconds > US_IN_MS) {
+ string = "ms";
+ divisor = US_IN_MS;
+ }
+
+ snprintf(label, BUCKET_LABEL_MAX_SIZE, "%4.2f%s", (float) microseconds / divisor,
+ string);
+}
+
+static void latency_tracker_bucket_parse(const struct latency_tracker *lt, int id,
+ __u32 lower_us, __u32 upper_us, bool upper_bounded)
+{
+ char buffer[BUCKET_LABEL_MAX_SIZE] = "";
+ __u32 bucket_data = le32_to_cpu(lt->stats.data[id]);
+
+ if (lt->print_flags == NORMAL) {
+
+ printf("%-*d", COL_WIDTH, id);
+
+ get_time_unit_label(buffer, lower_us, true);
+ printf("%-*s", COL_WIDTH, buffer);
+
+ get_time_unit_label(buffer, upper_us, upper_bounded);
+ printf("%-*s", COL_WIDTH, buffer);
+
+ printf("%-*d\n", COL_WIDTH, bucket_data);
+ }
+
+ if (lt->print_flags == JSON) {
+ /*
+ * Creates a bucket under the "values" json_object. Format is:
+ * "values" : {
+ * "bucket" : {
+ * "id" : #,
+ * "start" : string,
+ * "end" : string,
+ * "value" : 0,
+ * },
+ */
+ struct json_object *bucket = json_create_object();
+
+ json_object_array_add(lt->bucket_list, bucket);
+ json_object_add_value_int(bucket, "id", id);
+
+ get_time_unit_label(buffer, lower_us, true);
+ json_object_add_value_string(bucket, "start", buffer);
+
+ get_time_unit_label(buffer, upper_us, upper_bounded);
+ json_object_add_value_string(bucket, "end", buffer);
+
+ json_object_add_value_int(bucket, "value", bucket_data);
+ }
+}
+
+static void latency_tracker_parse_linear(const struct latency_tracker *lt,
+ __u32 start_offset, __u32 end_offset,
+ __u32 bytes_per, __u32 us_step,
+ bool nonzero_print)
+{
+ for (int i = (start_offset / bytes_per) - 1;
+ i < end_offset / bytes_per; i++) {
+ if (nonzero_print && lt->stats.data[i] == 0)
+ continue;
+ latency_tracker_bucket_parse(lt, i, us_step * i,
+ us_step * (i + 1), true);
+ }
+}
+
+/*
+ * Calculates bucket time slot. Valid starting on 4.0 revision.
+ */
+
+static int latency_tracker_bucket_pos2us(const struct latency_tracker *lt, int i)
+{
+ __u32 base_val = 1 << lt->base_range_bits;
+ if (i < (base_val << 1))
+ return i;
+
+ int error_bits = (i >> lt->base_range_bits) - 1;
+ int base = 1 << (error_bits + lt->base_range_bits);
+ int k = i % base_val;
+
+ return base + ((k + 0.5) * (1 << error_bits));
+}
+
+/*
+ * Creates a subroot in the following manner:
+ * {
+ * "latstats" : {
+ * "type" : "write" or "read",
+ * "values" : {
+ */
+static void latency_tracker_populate_json_root(const struct latency_tracker *lt,
+ struct json_object *root)
+{
+ struct json_object *subroot = json_create_object();
+
+ json_object_add_value_object(root, "latstats", subroot);
+ json_object_add_value_string(subroot, "type", lt->cfg.write ? "write" : "read");
+ if (lt->has_average_latency_field) {
+ json_object_add_value_uint64(subroot, "average_latency", le64_to_cpu(lt->stats.average_latency));
+ }
+ json_object_add_value_object(subroot, "values", lt->bucket_list);
+}
+
+static void latency_tracker_parse_3_0(const struct latency_tracker *lt)
+{
+ latency_tracker_parse_linear(lt, 4, 131, 4, 32, false);
+ latency_tracker_parse_linear(lt, 132, 255, 4, 1024, false);
+ latency_tracker_parse_linear(lt, 256, 379, 4, 32768, false);
+ latency_tracker_parse_linear(lt, 380, 383, 4, 32, true);
+ latency_tracker_parse_linear(lt, 384, 387, 4, 32, true);
+ latency_tracker_parse_linear(lt, 388, 391, 4, 32, true);
+}
+
+static void latency_tracker_parse_4_0(const struct latency_tracker *lt)
+{
+ for (unsigned int i = 0; i < lt->bucket_list_size; i++) {
+ int lower_us = latency_tracker_bucket_pos2us(lt, i);
+ int upper_us = latency_tracker_bucket_pos2us(lt, i + 1);
+
+ latency_tracker_bucket_parse(lt, i, lower_us,
+ upper_us,
+ i < (lt->bucket_list_size - 1));
+ }
+}
+
+static void print_dash_separator()
+{
+ printf("--------------------------------------------------\n");
+}
+
+static void latency_tracker_pre_parse(struct latency_tracker *lt)
+{
+ if (lt->print_flags == NORMAL) {
+ printf("Solidigm IO %s Command Latency Tracking Statistics type %d\n",
+ lt->cfg.write ? "Write" : "Read", lt->cfg.type);
+ printf("Major Revision: %u\nMinor Revision: %u\n",
+ le16_to_cpu(lt->stats.version_major), le16_to_cpu(lt->stats.version_minor));
+ if (lt->has_average_latency_field) {
+ printf("Average Latency: %" PRIu64 "\n", le64_to_cpu(lt->stats.average_latency));
+ }
+ print_dash_separator();
+ printf("%-12s%-12s%-12s%-20s\n", "Bucket", "Start", "End", "Value");
+ print_dash_separator();
+ }
+ if (lt->print_flags == JSON) {
+ lt->bucket_list = json_object_new_array();
+ }
+}
+
+static void latency_tracker_post_parse(struct latency_tracker *lt)
+{
+ if (lt->print_flags == JSON) {
+ struct json_object *root = json_create_object();
+
+ latency_tracker_populate_json_root(lt, root);
+ json_print_object(root, NULL);
+ json_free_object(root);
+ printf("\n");
+ }
+}
+
+static void latency_tracker_parse(struct latency_tracker *lt)
+{
+ __u16 version_major = le16_to_cpu(lt->stats.version_major);
+ __u16 version_minor = le16_to_cpu(lt->stats.version_minor);
+
+ switch (version_major) {
+ case 3:
+ latency_tracker_pre_parse(lt);
+ latency_tracker_parse_3_0(lt);
+ break;
+ case 4:
+ if (version_minor >= 8){
+ lt->has_average_latency_field = true;
+ }
+ latency_tracker_pre_parse(lt);
+ if (version_minor == 0){
+ lt->base_range_bits = BASE_RANGE_BITS_4_0;
+ lt->bucket_list_size = BUCKET_LIST_SIZE_4_0;
+ }
+ latency_tracker_parse_4_0(lt);
+ break;
+ default:
+ printf("Unsupported revision (%u.%u)\n",
+ version_major, version_minor);
+ break;
+ }
+
+ latency_tracker_post_parse(lt);
+}
+
+#define LATENCY_TRACKING_FID 0xe2
+#define LATENCY_TRACKING_FID_DATA_LEN 32
+
+static int latency_tracking_is_enable(struct latency_tracker *lt, __u32 * enabled)
+{
+ struct nvme_get_features_args args_get = {
+ .args_size = sizeof(args_get),
+ .fd = lt->fd,
+ .fid = LATENCY_TRACKING_FID,
+ .nsid = 0,
+ .sel = 0,
+ .cdw11 = 0,
+ .uuidx = 0,
+ .data_len = LATENCY_TRACKING_FID_DATA_LEN,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = enabled,
+ };
+ return nvme_get_features(&args_get);
+}
+
+static int latency_tracking_enable(struct latency_tracker *lt)
+{
+ __u32 result;
+ int err;
+
+ if (!(lt->cfg.enable || lt->cfg.disable)){
+ return 0;
+ }
+
+ if (lt->cfg.enable && lt->cfg.disable){
+ fprintf(stderr,"Cannot enable and disable simultaneously.\n");
+ return EINVAL;
+ }
+
+ struct nvme_set_features_args args_set = {
+ .args_size = sizeof(args_set),
+ .fd = lt->fd,
+ .fid = LATENCY_TRACKING_FID,
+ .nsid = 0,
+ .cdw11 = lt->cfg.enable,
+ .cdw12 = 0,
+ .save = 0,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = LATENCY_TRACKING_FID_DATA_LEN,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ err = nvme_set_features(&args_set);
+ if (err > 0) {
+ nvme_show_status(err);
+ } else if (err < 0) {
+ perror("Enable latency tracking");
+ fprintf(stderr, "Command failed while parsing.\n");
+ } else {
+ if (lt->print_flags == NORMAL) {
+ printf("Successfully set enable bit for FID (0x%X) to %i.\n",
+ LATENCY_TRACKING_FID, lt->cfg.enable);
+ }
+ }
+ return err;
+}
+
+#define READ_LOG_ID 0xc1
+#define WRITE_LOG_ID 0xc2
+
+static int latency_tracker_get_log(struct latency_tracker *lt)
+{
+ int err;
+
+ if (lt->cfg.read && lt->cfg.write){
+ fprintf(stderr,"Cannot capture read and write logs simultaneously.\n");
+ return EINVAL;
+ }
+
+ if (!(lt->cfg.read || lt->cfg.write))
+ return 0;
+
+ struct nvme_get_log_args args = {
+ .lpo = 0,
+ .result = NULL,
+ .log = &lt->stats,
+ .args_size = sizeof(args),
+ .fd = lt->fd,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .lid = lt->cfg.write ? WRITE_LOG_ID : READ_LOG_ID,
+ .len = sizeof(lt->stats),
+ .nsid = NVME_NSID_ALL,
+ .csi = NVME_CSI_NVM,
+ .lsi = NVME_LOG_LSI_NONE,
+ .lsp = lt->cfg.type,
+ .uuidx = NVME_UUID_NONE,
+ .rae = false,
+ .ot = false,
+ };
+
+ err = nvme_get_log(&args);
+ if (err)
+ return err;
+
+ if (lt->print_flags & BINARY)
+ d_raw((unsigned char *)&lt->stats,
+ sizeof(lt->stats));
+ else {
+ latency_tracker_parse(lt);
+ }
+ return err;
+}
+
+int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ const char *desc = "Get and Parse Solidigm Latency Tracking Statistics log.";
+ struct nvme_dev *dev;
+ __u32 enabled;
+ int err;
+
+ struct latency_tracker lt = {
+ .cfg = {
+ .output_format = "normal",
+ },
+ .base_range_bits = BASE_RANGE_BITS_4_1,
+ .bucket_list_size = BUCKET_LIST_SIZE_4_1,
+ .has_average_latency_field = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("enable", 'e', &lt.cfg.enable, "Enable Latency Tracking"),
+ OPT_FLAG("disable", 'd', &lt.cfg.disable, "Disable Latency Tracking"),
+ OPT_FLAG("read", 'r', &lt.cfg.read, "Get read statistics"),
+ OPT_FLAG("write", 'w', &lt.cfg.write, "Get write statistics"),
+ OPT_BYTE("type", 't', &lt.cfg.type, "Log type to get"),
+ OPT_FMT("output-format", 'o', &lt.cfg.output_format, output_format),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ lt.fd = dev_fd(dev);
+
+ lt.print_flags = validate_output_format(lt.cfg.output_format);
+ if (lt.print_flags == -EINVAL) {
+ fprintf(stderr, "Invalid output format '%s'\n", lt.cfg.output_format);
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ if (lt.cfg.type > 0xf) {
+ fprintf(stderr, "Invalid Log type value '%d'\n", lt.cfg.type);
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ if (lt.cfg.type && !(lt.cfg.read || lt.cfg.write)) {
+ fprintf(stderr, "Log type option valid only when retrieving statistics\n");
+ dev_close(dev);
+ return EINVAL;
+ }
+
+ err = latency_tracking_enable(&lt);
+ if (err){
+ dev_close(dev);
+ return err;
+ }
+
+ err = latency_tracker_get_log(&lt);
+ if (err){
+ dev_close(dev);
+ return err;
+ }
+
+ if ((lt.cfg.read || lt.cfg.write || lt.cfg.enable || lt.cfg.disable)) {
+ dev_close(dev);
+ return 0;
+ }
+
+ err = latency_tracking_is_enable(&lt, &enabled);
+ if (!err) {
+ if (lt.print_flags == JSON) {
+ struct json_object *root = json_create_object();
+ json_object_add_value_int(root,"enabled", enabled);
+ json_print_object(root, NULL);
+ json_free_object(root);
+ printf("\n");
+ } else if (lt.print_flags == BINARY) {
+ putchar(enabled);
+ } else {
+ printf(
+ "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n",
+ LATENCY_TRACKING_FID, enabled);
+ }
+ } else {
+ fprintf(stderr, "Could not read feature id 0xE2.\n");
+ }
+ /* Redundant close() to make static code analysis happy */
+ close(dev->direct.fd);
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/solidigm/solidigm-latency-tracking.h b/plugins/solidigm/solidigm-latency-tracking.h
new file mode 100644
index 0000000..9a763a9
--- /dev/null
+++ b/plugins/solidigm/solidigm-latency-tracking.h
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+int solidigm_get_latency_tracking_log(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin);
diff --git a/plugins/solidigm/solidigm-nvme.c b/plugins/solidigm/solidigm-nvme.c
new file mode 100644
index 0000000..b547035
--- /dev/null
+++ b/plugins/solidigm/solidigm-nvme.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "nvme.h"
+
+#define CREATE_CMD
+#include "solidigm-nvme.h"
+
+#include "solidigm-smart.h"
+#include "solidigm-garbage-collection.h"
+#include "solidigm-latency-tracking.h"
+#include "solidigm-telemetry.h"
+#include "plugins/ocp/ocp-clear-fw-update-history.h"
+
+static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return solidigm_get_additional_smart_log(argc, argv, cmd, plugin);
+}
+
+static int get_garbage_collection_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return solidigm_get_garbage_collection_log(argc, argv, cmd, plugin);
+}
+
+static int get_latency_tracking_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return solidigm_get_latency_tracking_log(argc, argv, cmd, plugin);
+}
+
+static int get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return solidigm_get_telemetry_log(argc, argv, cmd, plugin);
+}
+
+static int clear_fw_update_history(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ return ocp_clear_fw_update_history(argc, argv, cmd, plugin);
+}
diff --git a/plugins/solidigm/solidigm-nvme.h b/plugins/solidigm/solidigm-nvme.h
new file mode 100644
index 0000000..778dbf9
--- /dev/null
+++ b/plugins/solidigm/solidigm-nvme.h
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/solidigm/solidigm-nvme
+
+#if !defined(SOLIDIGM_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define SOLIDIGM_NVME
+
+#include "cmd.h"
+
+#define SOLIDIGM_PLUGIN_VERSION "0.7"
+
+PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve Solidigm SMART Log", get_additional_smart_log)
+ ENTRY("garbage-collect-log", "Retrieve Garbage Collection Log", get_garbage_collection_log)
+ ENTRY("latency-tracking-log", "Enable/Retrieve Latency tracking Log", get_latency_tracking_log)
+ ENTRY("parse-telemetry-log", "Parse Telemetry Log binary", get_telemetry_log)
+ ENTRY("clear-fw-activate-history",
+ "Clear firmware update history log (redirects to ocp plug-in)",
+ clear_fw_update_history)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/solidigm/solidigm-smart.c b/plugins/solidigm/solidigm-smart.c
new file mode 100644
index 0000000..77c26ac
--- /dev/null
+++ b/plugins/solidigm/solidigm-smart.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#include "solidigm-smart.h"
+
+struct __attribute__((packed)) nvme_additional_smart_log_item {
+ __u8 id;
+ __u8 _kp[2];
+ __u8 normalized;
+ __u8 _np;
+ union __attribute__((packed)) {
+ __u8 raw[6];
+ struct __attribute__((packed)) wear_level {
+ __le16 min;
+ __le16 max;
+ __le16 avg;
+ } wear_level;
+ struct __attribute__((packed)) thermal_throttle {
+ __u8 pct;
+ __u32 count;
+ } thermal_throttle;
+ } ;
+ __u8 _rp;
+} ;
+typedef struct nvme_additional_smart_log_item smart_log_item_t;
+
+#define VU_SMART_PAGE_SIZE 512
+#define VU_SMART_MAX_ITEMS VU_SMART_PAGE_SIZE / sizeof(smart_log_item_t)
+typedef struct vu_smart_log {
+ smart_log_item_t item[VU_SMART_MAX_ITEMS];
+} vu_smart_log_t;
+
+static char *id_to_name(__u8 id)
+{
+ switch (id) {
+ case 0x0D:
+ return "soft_ecc_error_rate";
+ case 0x05:
+ return "relocatable_sector_count";
+ case 0xAB:
+ return "program_fail_count";
+ case 0xAC:
+ return "erase_fail_count";
+ case 0xAD:
+ return "wear_leveling_count";
+ case 0xAE:
+ return "unexpected_power_loss";
+ 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 0xE5:
+ return "read_commands_in_flight_counter";
+ case 0xE6:
+ return "write_commands_in_flight_counter";
+ case 0xEA:
+ return "thermal_throttle_status";
+ case 0xF0:
+ return "retry_buffer_overflow_counter";
+ 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 0xFA:
+ return "host_read_collision_count";
+ case 0xFB:
+ return "host_write_collision_count";
+ case 0xFC:
+ return "xor_pass_count";
+ case 0xFD:
+ return "xor_fail_count";
+ case 0xFE:
+ return "xor_invoked_count";
+ default:
+ return "unknown";
+ }
+}
+
+static void smart_log_item_print(smart_log_item_t *item)
+{
+ if (!item->id) {
+ return;
+ }
+
+ printf("%#x %-45s %3d ",
+ item->id, id_to_name(item->id), item->normalized);
+
+ switch (item->id) {
+ case 0xAD:
+ printf("min: %u, max: %u, avg: %u\n",
+ le16_to_cpu(item->wear_level.min),
+ le16_to_cpu(item->wear_level.max),
+ le16_to_cpu(item->wear_level.avg));
+ return;
+ case 0xEA:
+ printf("%u%%, cnt: %u\n",
+ item->thermal_throttle.pct,
+ le32_to_cpu(item->thermal_throttle.count));
+ return;
+ default:
+ printf("%"PRIu64"\n", int48_to_long(item->raw));
+ }
+}
+
+static void smart_log_item_add_json(smart_log_item_t *item, struct json_object *dev_stats)
+{
+ struct json_object *entry_stats = json_create_object();
+
+ if (!item->id) {
+ return;
+ }
+
+ json_object_add_value_int(entry_stats, "normalized", item->normalized);
+
+ switch (item->id) {
+ case 0xAD:
+ json_object_add_value_int(entry_stats, "min", le16_to_cpu(item->wear_level.min));
+ json_object_add_value_int(entry_stats, "max", le16_to_cpu(item->wear_level.max));
+ json_object_add_value_int(entry_stats, "avg", le16_to_cpu(item->wear_level.avg));
+ break;
+ case 0xEA:
+ json_object_add_value_int(entry_stats, "percentage", item->thermal_throttle.pct);
+ json_object_add_value_int(entry_stats, "count", le32_to_cpu(item->thermal_throttle.count));
+ break;
+ default:
+ json_object_add_value_int(entry_stats, "raw", int48_to_long(item->raw));
+ }
+ json_object_add_value_object(dev_stats, id_to_name(item->id), entry_stats);
+}
+
+static void vu_smart_log_show_json(vu_smart_log_t *payload, unsigned int nsid, const char *devname)
+{
+ struct json_object *dev_stats = json_create_object();
+ smart_log_item_t *item = payload->item;
+ struct json_object *root;
+
+ for (int i = 0; i < VU_SMART_MAX_ITEMS; i++) {
+ smart_log_item_add_json(&item[i], dev_stats);
+ }
+
+ root = json_create_object();
+ json_object_add_value_string(root, "Solidigm SMART log", devname);
+ json_object_add_value_object(root, "Device stats", dev_stats);
+
+ json_print_object(root, NULL);
+ json_free_object(root);
+}
+
+static void vu_smart_log_show(vu_smart_log_t *payload, unsigned int nsid, const char *devname)
+{
+ smart_log_item_t *item = payload->item;
+
+ printf("Additional Smart Log for NVME device:%s namespace-id:%x\n",
+ devname, nsid);
+ printf("ID KEY Normalized Raw\n");
+
+ for (int i = 0; i < VU_SMART_MAX_ITEMS; i++) {
+ smart_log_item_print(&item[i]);
+ }
+}
+
+int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Get Solidigm vendor specific smart log (optionally, "\
+ "for the specified namespace), and show it.";
+ const int solidigm_vu_smart_log_id = 0xCA;
+ vu_smart_log_t smart_log_payload;
+ enum nvme_print_flags flags;
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ __u32 namespace_id;
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .namespace_id = NVME_NSID_ALL,
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, "(optional) desired namespace"),
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ flags = validate_output_format(cfg.output_format);
+ if (flags == -EINVAL) {
+ fprintf(stderr, "Invalid output format '%s'\n", cfg.output_format);
+ dev_close(dev);
+ return flags;
+ }
+
+ err = nvme_get_log_simple(dev_fd(dev), solidigm_vu_smart_log_id,
+ sizeof(smart_log_payload), &smart_log_payload);
+ if (!err) {
+ if (flags & JSON) {
+ vu_smart_log_show_json(&smart_log_payload,
+ cfg.namespace_id, dev->name);
+ } else if (flags & BINARY) {
+ d_raw((unsigned char *)&smart_log_payload, sizeof(smart_log_payload));
+ } else {
+ vu_smart_log_show(&smart_log_payload, cfg.namespace_id,
+ dev->name);
+ }
+ } else if (err > 0) {
+ nvme_show_status(err);
+ }
+
+ /* Redundant close() to make static code analysis happy */
+ close(dev->direct.fd);
+ dev_close(dev);
+ return err;
+}
+
diff --git a/plugins/solidigm/solidigm-smart.h b/plugins/solidigm/solidigm-smart.h
new file mode 100644
index 0000000..e19ebe5
--- /dev/null
+++ b/plugins/solidigm/solidigm-smart.h
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+int solidigm_get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin);
diff --git a/plugins/solidigm/solidigm-telemetry.c b/plugins/solidigm/solidigm-telemetry.c
new file mode 100644
index 0000000..84a4e2a
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "nvme-print.h"
+#include "solidigm-telemetry.h"
+#include "solidigm-telemetry/telemetry-log.h"
+#include "solidigm-telemetry/cod.h"
+#include "solidigm-telemetry/header.h"
+#include "solidigm-telemetry/config.h"
+#include "solidigm-telemetry/data-area.h"
+
+static int read_file2buffer(char *file_name, char **buffer, size_t *length)
+{
+ FILE *fd = fopen(file_name, "rb");
+
+ if (!fd)
+ return errno;
+
+ fseek(fd, 0, SEEK_END);
+ size_t length_bytes = ftell(fd);
+
+ fseek(fd, 0, SEEK_SET);
+
+ *buffer = malloc(length_bytes);
+ if (!*buffer) {
+ fclose(fd);
+ return errno;
+ }
+ *length = fread(*buffer, 1, length_bytes, fd);
+ fclose(fd);
+ return 0;
+}
+
+struct config {
+ __u32 host_gen;
+ bool ctrl_init;
+ int data_area;
+ char *cfg_file;
+ bool is_input_file;
+};
+
+int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Parse Solidigm Telemetry log";
+ const char *hgen = "Controls when to generate new host initiated report. Default value '1' generates new host initiated report, value '0' causes retrieval of existing log.";
+ const char *cgen = "Gather report generated by the controller.";
+ const char *dgen = "Pick which telemetry data area to report. Default is 3 to fetch areas 1-3. Valid options are 1, 2, 3, 4.";
+ const char *cfile = "JSON configuration file";
+ const char *sfile = "data source <device> is binary file containing log dump instead of block or character device";
+ struct nvme_dev *dev;
+
+ struct telemetry_log tl = {
+ .root = json_create_object(),
+ .log = NULL,
+ };
+
+ struct config cfg = {
+ .host_gen = 1,
+ .ctrl_init = false,
+ .data_area = 3,
+ .cfg_file = NULL,
+ .is_input_file = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("host-generate", 'g', &cfg.host_gen, hgen),
+ OPT_FLAG("controller-init", 'c', &cfg.ctrl_init, cgen),
+ OPT_UINT("data-area", 'd', &cfg.data_area, dgen),
+ OPT_FILE("config-file", 'j', &cfg.cfg_file, cfile),
+ OPT_FLAG("source-file", 's', &cfg.is_input_file, sfile),
+ OPT_END()
+ };
+
+ int err = argconfig_parse(argc, argv, desc, opts);
+
+ if (err)
+ goto ret;
+
+ if (cfg.is_input_file) {
+ if (optind >= argc) {
+ err = errno = EINVAL;
+ perror(argv[0]);
+ goto ret;
+ }
+ char *binary_file_name = argv[optind];
+
+ err = read_file2buffer(binary_file_name, (char **)&tl.log, &tl.log_size);
+ } else {
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ }
+ if (err)
+ goto ret;
+
+ if (cfg.host_gen > 1) {
+ SOLIDIGM_LOG_WARNING("Invalid host-generate value '%d'", cfg.host_gen);
+ err = EINVAL;
+ goto close_fd;
+ }
+
+ if (cfg.cfg_file) {
+ char *conf_str = 0;
+ size_t length = 0;
+
+ err = read_file2buffer(cfg.cfg_file, &conf_str, &length);
+ if (err) {
+ SOLIDIGM_LOG_WARNING("Failed to open JSON configuration file %s: %s!",
+ cfg.cfg_file, strerror(err));
+ goto close_fd;
+ }
+ json_tokener * jstok = json_tokener_new();
+
+ tl.configuration = json_tokener_parse_ex(jstok, conf_str, length);
+ if (jstok->err != json_tokener_success) {
+ SOLIDIGM_LOG_WARNING("Parsing error on JSON configuration file %s: %s (at offset %d)",
+ cfg.cfg_file,
+ json_tokener_error_desc(jstok->err),
+ jstok->char_offset);
+ json_tokener_free(jstok);
+ err = EINVAL;
+ goto close_fd;
+ }
+ json_tokener_free(jstok);
+ }
+
+ if (!cfg.is_input_file) {
+ if (cfg.ctrl_init)
+ err = nvme_get_ctrl_telemetry(dev_fd(dev), true,
+ &tl.log, cfg.data_area,
+ &tl.log_size);
+ else if (cfg.host_gen)
+ err = nvme_get_new_host_telemetry(dev_fd(dev), &tl.log,
+ cfg.data_area,
+ &tl.log_size);
+ else
+ err = nvme_get_host_telemetry(dev_fd(dev), &tl.log,
+ cfg.data_area,
+ &tl.log_size);
+
+ if (err < 0) {
+ SOLIDIGM_LOG_WARNING("get-telemetry-log: %s",
+ nvme_strerror(errno));
+ goto close_fd;
+ } else if (err > 0) {
+ nvme_show_status(err);
+ SOLIDIGM_LOG_WARNING("Failed to acquire telemetry log %d!", err);
+ goto close_fd;
+ }
+ }
+ solidigm_telemetry_log_header_parse(&tl);
+ if (cfg.cfg_file)
+ solidigm_telemetry_log_data_areas_parse(&tl, cfg.data_area);
+ else
+ solidigm_telemetry_log_cod_parse(&tl);
+
+ json_print_object(tl.root, NULL);
+ json_free_object(tl.root);
+ printf("\n");
+
+close_fd:
+ if (!cfg.is_input_file) {
+ /* Redundant close() to make static code analysis happy */
+ close(dev->direct.fd);
+ dev_close(dev);
+ }
+ret:
+ json_free_object(tl.configuration);
+ free(tl.log);
+ return err;
+}
diff --git a/plugins/solidigm/solidigm-telemetry.h b/plugins/solidigm/solidigm-telemetry.h
new file mode 100644
index 0000000..971ee2a
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry.h
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+int solidigm_get_telemetry_log(int argc, char **argv, struct command *cmd, struct plugin *plugin);
diff --git a/plugins/solidigm/solidigm-telemetry/cod.c b/plugins/solidigm/solidigm-telemetry/cod.c
new file mode 100644
index 0000000..be5685b
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/cod.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+#include "common.h"
+#include "cod.h"
+
+const char *oemDataMapDesc[] = {
+ "Media Read Count", //Uid 0x00
+ "Host Read count", //Uid 0x01
+ "Media Write Count", //Uid 0x02
+ "Host Write Count", //Uid 0x03
+ "Device Model", // 0x04
+ "Serial Number", // 0x05
+ "Firmware Revision", // 0x06
+ "Drive Status", // 0x07
+ "Minimum Temperature", // 0x08
+ "Maximum Temperature", // 0x09
+ "Power Loss Protection Status", // 0x0a
+ "Lifetime Unsafe Shutdown Count", // 0x0b
+ "Lifetime Power Cycle Count", // 0x0c
+ "Minimum Read Latency", // 0x0d
+ "Maximum Read Latency", // 0x0e
+ "Average Read Latency", // 0x0f
+ "Minimum Write Latency", // 0x10
+ "Maximum Write Latency", // 0x11
+ "Average Write Latency", // 0x12
+ "Grown Defects Count", // 0x13
+ "DQS Recovery Count", // 0x14
+ "Program Fail Count", // 0x15
+ "Erase Fail Count", // 0x16
+ "Defrag Writes in Progress Count", // 0x17
+ "Total Defrag Writes Count", // 0x18
+ "Max Die Offline Number", // 0x19
+ "Current Die Offline Number", // 0x1A
+ "XOR Enable Status", // 0x1B
+ "Media Life Used", // 0x1C
+ "Uncorrectable Error Count", // 0x1D
+ "Current Wear Range Delta", // 0x1E
+ "Read Errors Corrected by XOR", // 0x1F
+ "Background Data Refresh", // 0x20
+ "Pmic Vin History Data 1 Min", // 0x21
+ "Pmic Vin History Data 1 Max", // 0x22
+ "Pmic Vin History Data 1 Avg", // 0x23
+ "Pmic Vin History Data 2 Min", // 0x24
+ "Pmic Vin History Data 2 Max", // 0x25
+ "Pmic Vin History Data 2 Avg", // 0x26
+ "Pmic Vin History Data Total Readings", // 0x27
+ "All Time Current Max Wear Level", // 0x28
+ "Media Wear Remaining", // 0x29
+ "Total Non-Defrag Writes", // 0x2A
+ "Number of sectors relocated in reaction to an error" //Uid 0x2B = 43
+};
+
+static const char * getOemDataMapDescription(__u32 id)
+{
+ if (id < (sizeof(oemDataMapDesc) / sizeof(oemDataMapDesc[0]))) {
+ return oemDataMapDesc[id];
+ }
+ return "unknown";
+}
+
+#define OEMSIGNATURE 0x504D4443
+
+#pragma pack(push, cod, 1)
+struct cod_header
+{
+ uint32_t versionMajor;
+ uint32_t versionMinor;
+ uint32_t Signature; //!Fixed signature value (0x504D4443) for identification and validation
+ uint32_t MapSizeInBytes; //!Total size of the map data structure in bytes
+ uint32_t EntryCount; //!Total number of entries in the entry list
+ uint8_t Reserved[12];
+};
+
+struct cod_item
+{
+ uint32_t DataFieldMapUid; //!The data field unique identifier value
+ uint32_t reserved1 : 8;
+ uint32_t dataFieldType : 8;
+ uint32_t issigned : 1;
+ uint32_t bigEndian : 1;
+ uint32_t dataInvalid : 1;
+ uint32_t reserved2 : 13;
+ uint32_t DataFieldSizeInBytes;
+ uint8_t Reserved1[4];
+ uint64_t DataFieldOffset;
+ uint8_t Reserved2[8];
+};
+
+struct cod_map
+{
+ struct cod_header header;
+ struct cod_item items[];
+};
+
+#pragma pack(pop, cod)
+
+void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl)
+{
+ enum cod_field_type
+ {
+ INTEGER,
+ FLOAT,
+ STRING,
+ TWO_BYTE_ASCII,
+ FOUR_BYTE_ASCII,
+
+ UNKNOWN = 0xFF,
+ };
+ json_object *telemetry_header = NULL;
+ json_object *COD_offset = NULL;
+ json_object *reason_id = NULL;
+
+ if (!json_object_object_get_ex(tl->root, "telemetryHeader", &telemetry_header))
+ return;
+ if (!json_object_object_get_ex(telemetry_header, "reasonIdentifier", &reason_id))
+ return;
+ if (!json_object_object_get_ex(reason_id, "OemDataMapOffset", &COD_offset))
+ return;
+
+ __u64 offset = json_object_get_int(COD_offset);
+
+ if (offset == 0) {
+ return;
+ }
+
+ if ((offset + sizeof(struct cod_header)) > tl->log_size) {
+ SOLIDIGM_LOG_WARNING("Warning: COD map header out of bounds.");
+ return;
+ }
+
+ const struct cod_map *data = (struct cod_map *) (((__u8 *)tl->log ) + offset);
+
+ uint32_t signature = be32_to_cpu(data->header.Signature);
+ if ( signature != OEMSIGNATURE){
+ SOLIDIGM_LOG_WARNING("Warning: Unsupported COD data signature %x!", signature);
+ return;
+ }
+ if ((offset + data->header.MapSizeInBytes) > tl->log_size){
+ SOLIDIGM_LOG_WARNING("Warning: COD map data out of bounds.");
+ return;
+ }
+
+ json_object *cod = json_create_object();
+ json_object_object_add(tl->root, "cod", cod);
+
+ for (int i =0 ; i < data->header.EntryCount; i++) {
+ if ((offset + sizeof(struct cod_header) + (i + 1) * sizeof(struct cod_item)) >
+ tl->log_size){
+ SOLIDIGM_LOG_WARNING("Warning: COD data out of bounds at item %d!", i);
+ return;
+ }
+ struct cod_item item = data->items[i];
+ if (item.DataFieldOffset + item.DataFieldOffset > tl->log_size) {
+ continue;
+ }
+ if (item.dataInvalid) {
+ continue;
+ }
+ uint8_t *val = ((uint8_t *)tl->log )+ item.DataFieldOffset;
+ const char *key = getOemDataMapDescription(item.DataFieldMapUid);
+ switch(item.dataFieldType){
+ case(INTEGER):
+ if (item.issigned) {
+ json_object_object_add(cod, key,
+ json_object_new_int64(le64_to_cpu(*(uint64_t *)val)));
+ } else {
+ json_object_add_value_uint64(cod, key, le64_to_cpu(*(uint64_t *)val));
+ }
+ break;
+ case(FLOAT):
+ json_object_add_value_float(cod, key, *(float *) val);
+ break;
+ case(STRING):
+ json_object_object_add(cod, key,
+ json_object_new_string_len((const char *)val, item.DataFieldSizeInBytes));
+ break;
+ case(TWO_BYTE_ASCII):
+ json_object_object_add(cod, key,
+ json_object_new_string_len((const char *)val,2));
+ break;
+ case(FOUR_BYTE_ASCII):
+ json_object_object_add(cod, key,
+ json_object_new_string_len((const char *)val, 4));
+ break;
+ default:
+ SOLIDIGM_LOG_WARNING("Warning: Unknown COD field type (%d)", item.DataFieldMapUid);
+
+ }
+ }
+}
diff --git a/plugins/solidigm/solidigm-telemetry/cod.h b/plugins/solidigm/solidigm-telemetry/cod.h
new file mode 100644
index 0000000..032ccdc
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/cod.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "telemetry-log.h"
+void solidigm_telemetry_log_cod_parse(struct telemetry_log *tl);
diff --git a/plugins/solidigm/solidigm-telemetry/config.c b/plugins/solidigm/solidigm-telemetry/config.c
new file mode 100644
index 0000000..781d786
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/config.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+#include <stdbool.h>
+#include "util/json.h"
+#include <stdio.h>
+
+// max 16 bit unsigned integer nummber 65535
+#define MAX_16BIT_NUM_AS_STRING_SIZE 6
+
+static bool config_get_by_version(const json_object *obj, int version_major,
+ int version_minor, json_object **value)
+{
+ char str_key[MAX_16BIT_NUM_AS_STRING_SIZE];
+ char str_subkey[MAX_16BIT_NUM_AS_STRING_SIZE];
+
+ snprintf(str_key, sizeof(str_key), "%d", version_major);
+ snprintf(str_subkey, sizeof(str_subkey), "%d", version_minor);
+ json_object *major_obj = NULL;
+
+ if (!json_object_object_get_ex(obj, str_key, &major_obj))
+ return false;
+ if (!json_object_object_get_ex(major_obj, str_subkey, value))
+ return false;
+ return value != NULL;
+}
+
+bool solidigm_config_get_by_token_version(const json_object *obj, int token_id,
+ int version_major, int version_minor,
+ json_object **value)
+{
+ json_object *token_obj = NULL;
+ char str_key[MAX_16BIT_NUM_AS_STRING_SIZE];
+
+ snprintf(str_key, sizeof(str_key), "%d", token_id);
+ if (!json_object_object_get_ex(obj, str_key, &token_obj))
+ return false;
+ if (!config_get_by_version(token_obj, version_major, version_minor, value))
+ return false;
+ return value != NULL;
+}
diff --git a/plugins/solidigm/solidigm-telemetry/config.h b/plugins/solidigm/solidigm-telemetry/config.h
new file mode 100644
index 0000000..bea84fb
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/config.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+#include <stdbool.h>
+#include "util/json.h"
+
+bool solidigm_config_get_by_token_version(const json_object *obj, int key, int subkey, int subsubkey, json_object **value);
diff --git a/plugins/solidigm/solidigm-telemetry/data-area.c b/plugins/solidigm/solidigm-telemetry/data-area.c
new file mode 100644
index 0000000..7233e8f
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/data-area.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "common.h"
+#include "data-area.h"
+#include "config.h"
+#include <ctype.h>
+
+#define SIGNED_INT_PREFIX "int"
+#define BITS_IN_BYTE 8
+
+#define MAX_WARNING_SIZE 1024
+
+static bool telemetry_log_get_value(const struct telemetry_log *tl,
+ uint32_t offset_bit, uint32_t size_bit,
+ bool is_signed, json_object **val_obj)
+{
+ uint32_t offset_bit_from_byte;
+ uint32_t additional_size_byte;
+ uint32_t offset_byte;
+ uint32_t val;
+
+ if (size_bit == 0) {
+ char err_msg[MAX_WARNING_SIZE];
+
+ snprintf(err_msg, MAX_WARNING_SIZE,
+ "Value with size_bit=0 not supported.");
+ *val_obj = json_object_new_string(err_msg);
+
+ return false;
+ }
+ additional_size_byte = (size_bit - 1) ? (size_bit - 1) / BITS_IN_BYTE : 0;
+ offset_byte = offset_bit / BITS_IN_BYTE;
+
+ if (offset_byte > (tl->log_size - additional_size_byte)) {
+ char err_msg[MAX_WARNING_SIZE];
+
+ snprintf(err_msg, MAX_WARNING_SIZE,
+ "Value offset greater than binary size (%u > %zu).",
+ offset_byte, tl->log_size);
+ *val_obj = json_object_new_string(err_msg);
+
+ return false;
+ }
+
+ offset_bit_from_byte = offset_bit - (offset_byte * BITS_IN_BYTE);
+
+ if ((size_bit + offset_bit_from_byte) > (sizeof(uint64_t) * BITS_IN_BYTE)) {
+ char err_msg[MAX_WARNING_SIZE];
+
+ snprintf(err_msg, MAX_WARNING_SIZE,
+ "Value crossing 64 bit, byte aligned bounday, "
+ "not supported. size_bit=%u, offset_bit_from_byte=%u.",
+ size_bit, offset_bit_from_byte);
+ *val_obj = json_object_new_string(err_msg);
+
+ return false;
+ }
+
+ val = *(uint64_t *)(((char *)tl->log) + offset_byte);
+ val >>= offset_bit_from_byte;
+ if (size_bit < 64)
+ val &= (1ULL << size_bit) - 1;
+ if (is_signed) {
+ if (val >> (size_bit - 1))
+ val |= -1ULL << size_bit;
+ *val_obj = json_object_new_int64(val);
+ } else {
+ *val_obj = json_object_new_uint64(val);
+ }
+
+ return true;
+}
+
+static int telemetry_log_structure_parse(const struct telemetry_log *tl,
+ json_object *struct_def,
+ size_t parent_offset_bit,
+ json_object *output,
+ json_object *metadata)
+{
+ json_object *obj_arraySizeArray = NULL;
+ json_object *obj = NULL;
+ json_object *obj_memberList;
+ json_object *major_dimension;
+ json_object *sub_output;
+ bool is_enumeration = false;
+ bool has_member_list;
+ const char *type = "";
+ const char *name;
+ size_t array_rank;
+ size_t offset_bit;
+ size_t size_bit;
+ uint32_t linear_array_pos_bit;
+
+ if (!json_object_object_get_ex(struct_def, "name", &obj)) {
+ SOLIDIGM_LOG_WARNING("Warning: Structure definition missing property 'name': %s",
+ json_object_to_json_string(struct_def));
+ return -1;
+ }
+
+ name = json_object_get_string(obj);
+
+ if (metadata) {
+ json_object_get(obj);
+ json_object_object_add(metadata, "objName", obj);
+ }
+
+ if (json_object_object_get_ex(struct_def, "type", &obj))
+ type = json_object_get_string(obj);
+
+ if (!json_object_object_get_ex(struct_def, "offsetBit", &obj)) {
+ SOLIDIGM_LOG_WARNING("Warning: Structure definition missing "
+ "property 'offsetBit': %s",
+ json_object_to_json_string(struct_def));
+ return -1;
+ }
+
+ offset_bit = json_object_get_uint64(obj);
+
+ if (!json_object_object_get_ex(struct_def, "sizeBit", &obj)) {
+ SOLIDIGM_LOG_WARNING("Warning: Structure definition missing "
+ "property 'sizeBit': %s",
+ json_object_to_json_string(struct_def));
+ return -1;
+ }
+
+ size_bit = json_object_get_uint64(obj);
+
+ if (json_object_object_get_ex(struct_def, "enum", &obj))
+ is_enumeration = json_object_get_boolean(obj);
+
+ has_member_list = json_object_object_get_ex(struct_def,
+ "memberList",
+ &obj_memberList);
+
+ if (!json_object_object_get_ex(struct_def, "arraySize",
+ &obj_arraySizeArray)) {
+ SOLIDIGM_LOG_WARNING("Warning: Structure definition missing "
+ "property 'arraySize': %s",
+ json_object_to_json_string(struct_def));
+ return -1;
+ }
+
+ array_rank = json_object_array_length(obj_arraySizeArray);
+ if (array_rank == 0) {
+ SOLIDIGM_LOG_WARNING("Warning: Structure property 'arraySize' "
+ "don't support flexible array: %s",
+ json_object_to_json_string(struct_def));
+ return -1;
+ }
+ uint32_t array_size_dimension[array_rank];
+
+ for (size_t i = 0; i < array_rank; i++) {
+ json_object *dimension = json_object_array_get_idx(obj_arraySizeArray, i);
+
+ array_size_dimension[i] = json_object_get_uint64(dimension);
+ major_dimension = dimension;
+ }
+ if (array_rank > 1) {
+ uint32_t linear_pos_per_index = array_size_dimension[0];
+ uint32_t prev_index_offset_bit = 0;
+ json_object *dimension_output;
+
+ for (int i = 1; i < (array_rank - 1); i++)
+ linear_pos_per_index *= array_size_dimension[i];
+
+ dimension_output = json_create_array();
+ if (json_object_get_type(output) == json_type_array)
+ json_object_array_add(output, dimension_output);
+ else
+ json_object_add_value_array(output, name, dimension_output);
+
+ /*
+ * Make sure major_dimension object will not be
+ * deleted from memory when deleted from array
+ */
+ json_object_get(major_dimension);
+ json_object_array_del_idx(obj_arraySizeArray, array_rank - 1, 1);
+
+ for (int i = 0 ; i < array_size_dimension[0]; i++) {
+ json_object *sub_array = json_create_array();
+ size_t offset;
+
+ offset = parent_offset_bit + prev_index_offset_bit;
+
+ json_object_array_add(dimension_output, sub_array);
+ telemetry_log_structure_parse(tl, struct_def,
+ offset, sub_array, NULL);
+ prev_index_offset_bit += linear_pos_per_index * size_bit;
+ }
+
+ json_object_array_put_idx(obj_arraySizeArray, array_rank - 1,
+ major_dimension);
+
+ return 0;
+ }
+
+ linear_array_pos_bit = 0;
+ sub_output = output;
+
+ if (array_size_dimension[0] > 1) {
+ sub_output = json_create_array();
+ if (json_object_get_type(output) == json_type_array)
+ json_object_array_add(output, sub_output);
+ else
+ json_object_add_value_array(output, name, sub_output);
+ }
+
+ for (uint32_t j = 0; j < array_size_dimension[0]; j++) {
+ if (is_enumeration || !has_member_list) {
+ bool is_signed = !strncmp(type, SIGNED_INT_PREFIX, sizeof(SIGNED_INT_PREFIX)-1);
+ json_object *val_obj;
+ size_t offset;
+
+ offset = parent_offset_bit + offset_bit + linear_array_pos_bit;
+ if (telemetry_log_get_value(tl, offset, size_bit, is_signed, &val_obj)) {
+ if (array_size_dimension[0] > 1)
+ json_object_array_put_idx(sub_output, j, val_obj);
+ else
+ json_object_object_add(sub_output, name, val_obj);
+ } else {
+ SOLIDIGM_LOG_WARNING("Warning: %s From property '%s', "
+ "array index %u, structure definition: %s",
+ json_object_get_string(val_obj),
+ name, j, json_object_to_json_string(struct_def));
+ json_free_object(val_obj);
+ }
+ } else {
+ json_object *sub_sub_output = json_object_new_object();
+ int num_members;
+
+ if (array_size_dimension[0] > 1)
+ json_object_array_put_idx(sub_output, j, sub_sub_output);
+ else
+ json_object_add_value_object(sub_output, name, sub_sub_output);
+
+ num_members = json_object_array_length(obj_memberList);
+ for (int k = 0; k < num_members; k++) {
+ json_object *member = json_object_array_get_idx(obj_memberList, k);
+ size_t offset;
+
+ offset = parent_offset_bit + offset_bit + linear_array_pos_bit;
+ telemetry_log_structure_parse(tl, member, offset,
+ sub_sub_output, NULL);
+ }
+ }
+ linear_array_pos_bit += size_bit;
+ }
+ return 0;
+}
+
+static int telemetry_log_data_area_get_offset(const struct telemetry_log *tl,
+ enum nvme_telemetry_da da,
+ uint32_t *offset, uint32_t *size)
+{
+ uint32_t offset_blocks = 1;
+ uint32_t last_block = tl->log->dalb1;
+ uint32_t last;
+
+ switch (da) {
+ case NVME_TELEMETRY_DA_1:
+ offset_blocks = 1;
+ last_block = tl->log->dalb1;
+ break;
+ case NVME_TELEMETRY_DA_2:
+ offset_blocks = tl->log->dalb1 + 1;
+ last_block = tl->log->dalb2;
+ break;
+ case NVME_TELEMETRY_DA_3:
+ offset_blocks = tl->log->dalb2 + 1;
+ last_block = tl->log->dalb3;
+ break;
+ case NVME_TELEMETRY_DA_4:
+ offset_blocks = tl->log->dalb3 + 1;
+ last_block = tl->log->dalb4;
+ break;
+ default:
+ return -1;
+ }
+
+ *offset = offset_blocks * NVME_LOG_TELEM_BLOCK_SIZE;
+ last = (last_block + 1) * NVME_LOG_TELEM_BLOCK_SIZE;
+ *size = last - *offset;
+ if ((*offset > tl->log_size) || (last > tl->log_size) || (last <= *offset)) {
+ SOLIDIGM_LOG_WARNING("Warning: Data Area %d don't fit this Telemetry log.", da);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct toc_item {
+ uint32_t OffsetBytes;
+ uint32_t ContentSizeBytes;
+};
+
+struct data_area_header {
+ uint8_t versionMajor;
+ uint8_t versionMinor;
+ uint16_t TableOfContentsCount;
+ uint32_t DataAreaSize;
+ uint8_t Reserved[8];
+};
+
+struct table_of_contents {
+ struct data_area_header header;
+ struct toc_item items[];
+};
+
+struct telemetry_object_header {
+ uint16_t versionMajor;
+ uint16_t versionMinor;
+ uint32_t Token;
+ uint8_t CoreId;
+ uint8_t Reserved[3];
+};
+
+
+static void telemetry_log_data_area_toc_parse(const struct telemetry_log *tl,
+ enum nvme_telemetry_da da,
+ json_object *toc_array,
+ json_object *tele_obj_array)
+{
+
+ const struct telemetry_object_header *header;
+ const struct table_of_contents *toc;
+ char *payload;
+ uint32_t da_offset;
+ uint32_t da_size;
+
+ if (telemetry_log_data_area_get_offset(tl, da, &da_offset, &da_size))
+ return;
+
+ toc = (struct table_of_contents *)(((char *)tl->log) + da_offset);
+ payload = (char *) tl->log;
+
+ for (int i = 0; i < toc->header.TableOfContentsCount; i++) {
+ json_object *structure_definition = NULL;
+ json_object *toc_item;
+ uint32_t obj_offset;
+ bool has_struct;
+
+ if ((char *)&toc->items[i] > (((char *)toc) + da_size - sizeof(const struct toc_item))) {
+ SOLIDIGM_LOG_WARNING("Warning: Data Area %d, "
+ "Table of Contents item %d "
+ "crossed Data Area size.", da, i);
+ return;
+ }
+
+ obj_offset = toc->items[i].OffsetBytes;
+ if ((obj_offset + sizeof(const struct telemetry_object_header)) > da_size) {
+ SOLIDIGM_LOG_WARNING("Warning: Data Area %d, item %d "
+ "data, crossed Data Area size.", da, i);
+ continue;
+ }
+
+ toc_item = json_create_object();
+ json_object_array_add(toc_array, toc_item);
+ json_object_add_value_uint(toc_item, "dataArea", da);
+ json_object_add_value_uint(toc_item, "dataAreaIndex", i);
+ json_object_add_value_uint(toc_item, "dataAreaOffset", obj_offset);
+ json_object_add_value_uint(toc_item, "fileOffset", obj_offset + da_offset);
+ json_object_add_value_uint(toc_item, "size", toc->items[i].ContentSizeBytes);
+
+ header = (const struct telemetry_object_header *) (payload + da_offset + obj_offset);
+ json_object_add_value_uint(toc_item, "telemMajor", header->versionMajor);
+ json_object_add_value_uint(toc_item, "telemMinor", header->versionMinor);
+ json_object_add_value_uint(toc_item, "objectId", header->Token);
+ json_object_add_value_uint(toc_item, "mediaBankId", header->CoreId);
+
+ has_struct = solidigm_config_get_by_token_version(tl->configuration,
+ header->Token,
+ header->versionMajor,
+ header->versionMinor,
+ &structure_definition);
+
+ if (has_struct) {
+ json_object *tele_obj_item = json_create_object();
+
+ json_object_array_add(tele_obj_array, tele_obj_item);
+ json_object_get(toc_item);
+ json_object_add_value_object(tele_obj_item, "metadata", toc_item);
+ json_object *parsed_struct = json_object_new_object();
+
+ json_object_add_value_object(tele_obj_item, "objectData", parsed_struct);
+ json_object *obj_hasTelemObjHdr = NULL;
+ uint32_t header_offset = sizeof(const struct telemetry_object_header);
+ uint32_t file_offset;
+
+ if (json_object_object_get_ex(structure_definition,
+ "hasTelemObjHdr",
+ &obj_hasTelemObjHdr)) {
+ bool hasHeader = json_object_get_boolean(obj_hasTelemObjHdr);
+
+ if (hasHeader)
+ header_offset = 0;
+ }
+
+ file_offset = da_offset + obj_offset + header_offset;
+ telemetry_log_structure_parse(tl, structure_definition,
+ BITS_IN_BYTE * file_offset,
+ parsed_struct, toc_item);
+ }
+ }
+}
+
+int solidigm_telemetry_log_data_areas_parse(const struct telemetry_log *tl,
+ enum nvme_telemetry_da last_da)
+{
+ json_object *tele_obj_array = json_create_array();
+ json_object *toc_array = json_create_array();
+
+ json_object_add_value_array(tl->root, "tableOfContents", toc_array);
+ json_object_add_value_array(tl->root, "telemetryObjects", tele_obj_array);
+
+ for (enum nvme_telemetry_da da = NVME_TELEMETRY_DA_1; da <= last_da; da++)
+ telemetry_log_data_area_toc_parse(tl, da, toc_array, tele_obj_array);
+
+ return 0;
+}
diff --git a/plugins/solidigm/solidigm-telemetry/data-area.h b/plugins/solidigm/solidigm-telemetry/data-area.h
new file mode 100644
index 0000000..095eb64
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/data-area.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+#include "common.h"
+#include "telemetry-log.h"
+
+int solidigm_telemetry_log_data_areas_parse(const struct telemetry_log *tl,
+ enum nvme_telemetry_da last_da);
diff --git a/plugins/solidigm/solidigm-telemetry/header.c b/plugins/solidigm/solidigm-telemetry/header.c
new file mode 100644
index 0000000..72b2d97
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/header.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "common.h"
+#include "header.h"
+
+#pragma pack(push, reason_indentifier, 1)
+struct reason_indentifier_1_0
+{
+ uint16_t versionMajor;
+ uint16_t versionMinor;
+ uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue.
+ char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020")
+ char FirmwareVersion[12]; //! Similar to IdentifyController.FR
+ char BootloaderVersion[12]; //! Bootloader version string
+ char SerialNumber[20]; //! Device serial number
+ uint8_t Reserved[56]; //! Reserved for future usage
+};
+static_assert(sizeof(const struct reason_indentifier_1_0) ==
+ MEMBER_SIZE(struct nvme_telemetry_log, rsnident),
+ "Size mismatch for reason_indentifier_1_0");
+
+struct reason_indentifier_1_1
+{
+ uint16_t versionMajor;
+ uint16_t versionMinor;
+ uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue.
+ char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020")
+ char FirmwareVersion[12]; //! Similar to IdentifyController.FR
+ char BootloaderVersion[12]; //! Bootloader version string
+ char SerialNumber[20]; //! Device serial number
+ uint64_t OemDataMapOffset; //! Customer Data Map Object Log Offset
+ uint8_t TelemetryMajorVersion; //! Shadow of version in TOC
+ uint8_t TelemetryMinorVersion; //! Shadow of version in TOC
+ uint8_t Reserved[46]; //! Reserved for future usage
+};
+static_assert(sizeof(const struct reason_indentifier_1_1) ==
+ MEMBER_SIZE(struct nvme_telemetry_log, rsnident),
+ "Size mismatch for reason_indentifier_1_1");
+
+struct reason_indentifier_1_2
+{
+ uint16_t versionMajor;
+ uint16_t versionMinor;
+ uint32_t reasonCode; //! 0 denotes no issue. All other values denote a potential issue.
+ char DriveStatus[20]; //! Drive Status String (for example: "Healthy", "*BAD_CONTEXT_2020")
+ uint8_t Reserved1[24]; //! pad over Fields removed from version 1.1
+ char SerialNumber[20]; //! Device serial number
+ uint64_t OemDataMapOffset; //! Customer Data Map Object Log Offset
+ uint8_t TelemetryMajorVersion; //! Shadow of version in TOC
+ uint8_t TelemetryMinorVersion; //! Shadow of version in TOC
+ uint8_t ProductFamilyId;
+ uint8_t Reserved2[5]; //! Reserved for future usage
+ uint8_t DualPortReserved[40]; //! Reserved for dual port
+};
+static_assert(sizeof(const struct reason_indentifier_1_2) ==
+ MEMBER_SIZE(struct nvme_telemetry_log, rsnident),
+ "Size mismatch for reason_indentifier_1_2");
+#pragma pack(pop, reason_indentifier)
+
+static void telemetry_log_reason_id_parse1_0_ext(const struct telemetry_log *tl,
+ json_object *reason_id)
+{
+ const struct reason_indentifier_1_0 *ri;
+ json_object *reserved;
+
+ ri = (struct reason_indentifier_1_0 *) tl->log->rsnident;
+ json_object_object_add(reason_id, "FirmwareVersion", json_object_new_string_len(ri->FirmwareVersion, sizeof(ri->FirmwareVersion)));
+ json_object_object_add(reason_id, "BootloaderVersion", json_object_new_string_len(ri->BootloaderVersion, sizeof(ri->BootloaderVersion)));
+ json_object_object_add(reason_id, "SerialNumber", json_object_new_string_len(ri->SerialNumber, sizeof(ri->SerialNumber)));
+
+ reserved = json_create_array();
+ json_object_add_value_array(reason_id, "Reserved", reserved);
+ for ( int i=0; i < sizeof(ri->Reserved); i++) {
+ json_object *val = json_object_new_int(ri->Reserved[i]);
+ json_object_array_add(reserved, val);
+ }
+}
+
+static void telemetry_log_reason_id_parse1_1_ext(const struct telemetry_log *tl,
+ json_object *reason_id)
+{
+ const struct reason_indentifier_1_1 *ri;
+ json_object *reserved;
+
+ ri = (struct reason_indentifier_1_1 *) tl->log->rsnident;
+ json_object_object_add(reason_id, "FirmwareVersion", json_object_new_string_len(ri->FirmwareVersion, sizeof(ri->FirmwareVersion)));
+ json_object_object_add(reason_id, "BootloaderVersion", json_object_new_string_len(ri->BootloaderVersion, sizeof(ri->BootloaderVersion)));
+ json_object_object_add(reason_id, "SerialNumber", json_object_new_string_len(ri->SerialNumber, sizeof(ri->SerialNumber)));
+ json_object_add_value_uint64(reason_id, "OemDataMapOffset", le64_to_cpu(ri->OemDataMapOffset));
+ json_object_add_value_uint(reason_id, "TelemetryMajorVersion", le16_to_cpu(ri->TelemetryMajorVersion));
+ json_object_add_value_uint(reason_id, "TelemetryMinorVersion", le16_to_cpu(ri->TelemetryMinorVersion));
+
+ reserved = json_create_array();
+ json_object_add_value_array(reason_id, "Reserved", reserved);
+ for (int i = 0; i < sizeof(ri->Reserved); i++) {
+ json_object *val = json_object_new_int(ri->Reserved[i]);
+ json_object_array_add(reserved, val);
+ }
+}
+
+static void telemetry_log_reason_id_parse1_2_ext(const struct telemetry_log *tl,
+ json_object *reason_id)
+{
+ const struct reason_indentifier_1_2 *ri;
+ json_object *dp_reserved;
+ json_object *reserved;
+
+ ri = (struct reason_indentifier_1_2 *) tl->log->rsnident;
+
+ json_object_object_add(reason_id, "SerialNumber", json_object_new_string_len(ri->SerialNumber, sizeof(ri->SerialNumber)));
+ json_object_add_value_uint64(reason_id, "OemDataMapOffset", le64_to_cpu(ri->OemDataMapOffset));
+ json_object_add_value_uint(reason_id, "TelemetryMajorVersion", le16_to_cpu(ri->TelemetryMajorVersion));
+ json_object_add_value_uint(reason_id, "TelemetryMinorVersion", le16_to_cpu(ri->TelemetryMinorVersion));
+ json_object_add_value_uint(reason_id, "ProductFamilyId", ri->ProductFamilyId);
+
+ reserved = json_create_array();
+ json_object_add_value_array(reason_id, "Reserved2", reserved);
+ for (int i = 0; i < sizeof(ri->Reserved2); i++) {
+ json_object *val = json_object_new_int(ri->Reserved2[i]);
+ json_object_array_add(reserved, val);
+ }
+
+ dp_reserved = json_create_array();
+ json_object_add_value_array(reason_id, "DualPortReserved", dp_reserved);
+ for (int i = 0; i < sizeof(ri->DualPortReserved); i++) {
+ json_object *val = json_object_new_int(ri->DualPortReserved[i]);
+ json_object_array_add(dp_reserved, val);
+ }
+}
+
+static void solidigm_telemetry_log_reason_id_parse(const struct telemetry_log *tl, json_object *reason_id)
+{
+ const struct reason_indentifier_1_0 *ri1_0 =
+ (struct reason_indentifier_1_0 *) tl->log->rsnident;
+ __u16 version_major = le16_to_cpu(ri1_0->versionMajor);
+ __u16 version_minor = le16_to_cpu(ri1_0->versionMinor);
+
+ json_object_add_value_uint(reason_id, "versionMajor", version_major);
+ json_object_add_value_uint(reason_id, "versionMinor", version_minor);
+ json_object_add_value_uint(reason_id, "reasonCode", le32_to_cpu(ri1_0->reasonCode));
+ json_object_add_value_object(reason_id, "DriveStatus", json_object_new_string_len(ri1_0->DriveStatus, sizeof(ri1_0->DriveStatus)));
+ if (version_major == 1) {
+ switch (version_minor) {
+ case 0:
+ telemetry_log_reason_id_parse1_0_ext(tl, reason_id);
+ break;
+ case 1:
+ telemetry_log_reason_id_parse1_1_ext(tl, reason_id);
+ break;
+ default:
+ telemetry_log_reason_id_parse1_2_ext(tl, reason_id);
+ }
+ }
+}
+
+bool solidigm_telemetry_log_header_parse(const struct telemetry_log *tl)
+{
+ const struct nvme_telemetry_log *log;
+ json_object *ieee_oui_id;
+ json_object *reason_id;
+ json_object *header;
+
+ if (tl->log_size < sizeof(const struct nvme_telemetry_log)) {
+ SOLIDIGM_LOG_WARNING("Telemetry log too short.");
+ return false;
+ }
+
+ header = json_create_object();
+
+ json_object_object_add(tl->root, "telemetryHeader", header);
+ log = tl->log;
+
+ json_object_add_value_uint(header, "logIdentifier", log->lpi);
+ ieee_oui_id = json_create_array();
+
+ json_object_object_add(header, "ieeeOuiIdentifier", ieee_oui_id);
+ for (int i = 0; i < sizeof(log->ieee); i++) {
+ json_object *val = json_object_new_int(log->ieee[i]);
+
+ json_object_array_add(ieee_oui_id, val);
+ }
+ json_object_add_value_uint(header, "dataArea1LastBlock", log->dalb1);
+ json_object_add_value_uint(header, "dataArea2LastBlock", log->dalb2);
+ json_object_add_value_uint(header, "dataArea3LastBlock", log->dalb3);
+ json_object_add_value_uint(header, "hostInitiatedDataGeneration", log->hostdgn);
+ json_object_add_value_uint(header, "controllerInitiatedDataAvailable", log->ctrlavail);
+ json_object_add_value_uint(header, "controllerInitiatedDataGeneration", log->ctrldgn);
+
+ reason_id = json_create_object();
+ json_object_add_value_object(header, "reasonIdentifier", reason_id);
+ solidigm_telemetry_log_reason_id_parse(tl, reason_id);
+
+ return true;
+}
diff --git a/plugins/solidigm/solidigm-telemetry/header.h b/plugins/solidigm/solidigm-telemetry/header.h
new file mode 100644
index 0000000..027af55
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/header.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#include "telemetry-log.h"
+bool solidigm_telemetry_log_header_parse(const struct telemetry_log *tl);
diff --git a/plugins/solidigm/solidigm-telemetry/meson.build b/plugins/solidigm/solidigm-telemetry/meson.build
new file mode 100644
index 0000000..53ab452
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/meson.build
@@ -0,0 +1,6 @@
+sources += [
+ 'plugins/solidigm/solidigm-telemetry/cod.c',
+ 'plugins/solidigm/solidigm-telemetry/header.c',
+ 'plugins/solidigm/solidigm-telemetry/config.c',
+ 'plugins/solidigm/solidigm-telemetry/data-area.c',
+]
diff --git a/plugins/solidigm/solidigm-telemetry/telemetry-log.h b/plugins/solidigm/solidigm-telemetry/telemetry-log.h
new file mode 100644
index 0000000..ef4ec5d
--- /dev/null
+++ b/plugins/solidigm/solidigm-telemetry/telemetry-log.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2022 Solidigm.
+ *
+ * Author: leonardo.da.cunha@solidigm.com
+ */
+
+#ifndef _SOLIDIGM_TELEMETRY_LOG_H
+#define _SOLIDIGM_TELEMETRY_LOG_H
+
+#include "libnvme.h"
+#include "util/json.h"
+#include <assert.h>
+
+#if !defined __cplusplus
+#define static_assert _Static_assert
+#endif
+
+#define VA_ARGS(...), ##__VA_ARGS__
+#define SOLIDIGM_LOG_WARNING(format, ...) fprintf(stderr, format"\n" VA_ARGS(__VA_ARGS__))
+
+#define MEMBER_SIZE(type, member) sizeof(((type *)0)->member)
+
+struct telemetry_log {
+ struct nvme_telemetry_log *log;
+ size_t log_size;
+ json_object *root;
+ json_object *configuration;
+};
+
+#endif /* _SOLIDIGM_TELEMETRY_LOG_H */ \ No newline at end of file
diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c
new file mode 100644
index 0000000..5540fea
--- /dev/null
+++ b/plugins/toshiba/toshiba-nvme.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "toshiba-nvme.h"
+
+static const __u32 OP_SCT_STATUS = 0xE0;
+static const __u32 OP_SCT_COMMAND_TRANSFER = 0xE0;
+static const __u32 OP_SCT_DATA_TRANSFER = 0xE1;
+
+static const __u32 DW10_SCT_STATUS_COMMAND = 0x0;
+static const __u32 DW10_SCT_COMMAND_TRANSFER = 0x1;
+
+static const __u32 DW11_SCT_STATUS_COMMAND = 0x0;
+static const __u32 DW11_SCT_COMMAND_TRANSFER = 0x0;
+
+static const __u16 INTERNAL_LOG_ACTION_CODE = 0xFFFB;
+static const __u16 CURRENT_LOG_FUNCTION_CODE = 0x0001;
+static const __u16 SAVED_LOG_FUNCTION_CODE = 0x0002;
+
+/* A bitmask field for supported devices */
+typedef enum {
+ MASK_0 = 1 << 0,
+ MASK_1 = 1 << 1,
+ /*
+ * Future devices can use the remaining 31 bits from this field
+ * and should use 1 << 2, 1 << 3, etc.
+ */
+ MASK_IGNORE = 0
+} DeviceMask;
+
+/* Internal device codes */
+typedef enum {
+ CODE_0 = 0x0D,
+ CODE_1 = 0x10
+} DeviceCode;
+
+
+static int nvme_sct_op(int fd, __u32 opcode, __u32 cdw10, __u32 cdw11, void* data, __u32 data_len )
+{
+ void *metadata = NULL;
+ const __u32 cdw2 = 0;
+ const __u32 cdw3 = 0;
+ const __u32 cdw12 = 0;
+ const __u32 cdw13 = 0;
+ const __u32 cdw14 = 0;
+ const __u32 cdw15 = 0;
+ const __u32 timeout = 0;
+ const __u32 metadata_len = 0;
+ const __u32 namespace_id = 0x0;
+ const __u32 flags = 0;
+ const __u32 rsvd = 0;
+ int err = 0;
+
+ __u32 result;
+ err = nvme_admin_passthru(fd, opcode, flags, rsvd,
+ namespace_id, cdw2, cdw3, cdw10,
+ cdw11, cdw12, cdw13, cdw14, cdw15,
+ data_len, data, metadata_len, metadata,
+ timeout, &result);
+ return err;
+}
+
+static int nvme_get_sct_status(int fd, __u32 device_mask)
+{
+ int err;
+ void* data = NULL;
+ size_t data_len = 512;
+ unsigned char *status;
+
+ if (posix_memalign(&data, getpagesize(), data_len))
+ return ENOMEM;
+
+ memset(data, 0, data_len);
+ err = nvme_sct_op(fd, OP_SCT_STATUS, DW10_SCT_STATUS_COMMAND, DW11_SCT_STATUS_COMMAND, data, data_len);
+ if (err) {
+ fprintf(stderr, "%s: SCT status failed :%d\n", __func__, err);
+ goto end;
+ }
+
+ status = data;
+ if (status[0] != 1U) {
+ /* Eek, wrong version in status header */
+ fprintf(stderr, "%s: unexpected value in SCT status[0]:(%x)\n", __func__, status[0]);
+ err = -1;
+ errno = EINVAL;
+ goto end;
+ }
+
+ /* Check if device is supported */
+ if (device_mask != MASK_IGNORE) {
+ __u32 supported = 0;
+ switch (status[1]) {
+ case CODE_0:
+ supported = (device_mask & MASK_0);
+ break;
+
+ case CODE_1:
+ supported = (device_mask & MASK_1);
+ break;
+
+ default:
+ break;
+ };
+
+ if (0 == supported) {
+ fprintf(stderr, "%s: command unsupported on this device: (0x%x)\n",__func__, status[1]);
+ err = -1;
+ errno = EINVAL;
+ goto end;
+ }
+ }
+end:
+ if (data)
+ free(data);
+ return err;
+}
+
+static int nvme_sct_command_transfer_log(int fd, bool current)
+{
+ int err;
+ void *data = NULL;
+ size_t data_len = 512;
+ __u16 function_code, action_code = INTERNAL_LOG_ACTION_CODE;
+
+ if (current)
+ function_code = CURRENT_LOG_FUNCTION_CODE;
+ else
+ function_code = SAVED_LOG_FUNCTION_CODE;
+
+ if (posix_memalign(&data, getpagesize(), data_len))
+ return ENOMEM;
+
+ memset(data, 0, data_len);
+ memcpy(data, &action_code, sizeof(action_code));
+ memcpy(data + 2, &function_code, sizeof(function_code));
+
+ err = nvme_sct_op(fd, OP_SCT_COMMAND_TRANSFER, DW10_SCT_COMMAND_TRANSFER, DW11_SCT_COMMAND_TRANSFER, data, data_len);
+ free(data);
+ return err;
+}
+
+static int nvme_sct_data_transfer(int fd, void* data, size_t data_len, size_t offset)
+{
+ __u32 dw10, dw11, lba_count = (data_len) / 512;
+
+ if (lba_count) {
+ /*
+ * the count is a 0-based value, which seems to mean
+ * that it's actually last lba
+ */
+ --lba_count;
+ }
+
+ dw10 = (offset << 16) | lba_count;
+ dw11 = (offset >> 16);
+ return nvme_sct_op(fd, OP_SCT_DATA_TRANSFER, dw10, dw11, data, data_len);
+}
+
+static int d_raw_to_fd(const unsigned char *buf, unsigned len, int fd)
+{
+ int written = 0;
+ int remaining = len;
+
+ while (remaining) {
+ written = write(fd, buf, remaining);
+ if (written < 0) {
+ remaining = written;
+ break;
+ } else if (written <= remaining) {
+ remaining -= written;
+ } else {
+ /* Unexpected overwrite */
+ break;
+ }
+ }
+
+ /* return 0 on success or remaining/error */
+ return remaining;
+}
+
+/* Display progress (incoming 0->1.0) */
+static void progress_runner(float progress)
+{
+ const size_t barWidth = 70;
+ size_t i, pos;
+
+ fprintf(stdout, "[");
+ pos = barWidth * progress;
+ for (i = 0; i < barWidth; ++i) {
+ if (i <= pos)
+ fprintf(stdout, "=");
+ else
+ fprintf(stdout, " ");
+ }
+
+ fprintf(stdout, "] %d %%\r",(int)(progress * 100.0));
+ fflush(stdout);
+}
+
+static int nvme_get_internal_log(int fd, const char* const filename, bool current)
+{
+ int err;
+ int o_fd = -1;
+ void* page_data = NULL;
+ const size_t page_sector_len = 32;
+ const size_t page_data_len = page_sector_len * 512; /* 32 sectors per page */
+ uint32_t* area1_last_page;
+ uint32_t* area2_last_page;
+ uint32_t* area3_last_page;
+ uint32_t log_sectors = 0;
+ size_t pages;
+
+ /*
+ * By trial and error it seems that the largest transfer chunk size
+ * is 128 * 32 = 4k sectors = 2MB
+ */
+ const __u32 max_pages = 128;
+ size_t i;
+ unsigned int j;
+ float progress = 0.0;
+
+ err = nvme_sct_command_transfer_log(fd, current);
+ if (err) {
+ fprintf(stderr, "%s: SCT command transfer failed\n", __func__);
+ goto end;
+ }
+
+ if (posix_memalign(&page_data, getpagesize(), max_pages * page_data_len)) {
+ err = ENOMEM;
+ goto end;
+ }
+ memset(page_data, 0, max_pages * page_data_len);
+
+ /* Read the header to get the last log page - offsets 8->11, 12->15, 16->19 */
+ err = nvme_sct_data_transfer(fd, page_data, page_data_len, 0);
+ if (err) {
+ fprintf(stderr, "%s: SCT data transfer failed, page 0\n",__func__);
+ goto end;
+ }
+
+ area1_last_page = (uint32_t *) (page_data + 8);
+ area2_last_page = (uint32_t *) (page_data + 12);
+ area3_last_page = (uint32_t *) (page_data + 16);
+
+ /* The number of total log sectors is the maximum + 1; */
+ if (*area1_last_page > log_sectors)
+ log_sectors = *area1_last_page;
+ if (*area2_last_page > log_sectors)
+ log_sectors = *area2_last_page;
+ if (*area3_last_page > log_sectors)
+ log_sectors = *area3_last_page;
+
+ ++log_sectors;
+ pages = log_sectors / page_sector_len;
+ if (filename == NULL) {
+ fprintf(stdout, "Page: %u of %zu\n", 0u, pages);
+ d(page_data, page_data_len, 16, 1);
+ } else {
+ progress_runner(progress);
+ o_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (o_fd < 0) {
+ fprintf(stderr, "%s: couldn't output file %s\n", __func__, filename);
+ err = EINVAL;
+ goto end;
+ }
+ err = d_raw_to_fd(page_data, page_data_len, o_fd);
+ if (err) {
+ fprintf(stderr, "%s: couldn't write all data to output file\n", __func__);
+ goto end;
+ }
+ }
+
+ /* Now read the rest */
+ for (i = 1; i < pages;) {
+ __u32 pages_chunk = max_pages;
+ if (pages_chunk + i >= pages)
+ pages_chunk = pages - i;
+
+ err = nvme_sct_data_transfer(fd, page_data,
+ pages_chunk * page_data_len,
+ i * page_sector_len);
+ if (err) {
+ fprintf(stderr, "%s: SCT data transfer command failed\n", __func__);
+ goto end;
+ }
+
+ progress = (float) (i) / (float) (pages);
+ progress_runner(progress);
+ if (filename == NULL) {
+ for (j = 0; j < pages_chunk; ++j) {
+ fprintf(stdout, "Page: %zu of %zu\n", i + j, pages);
+ d(page_data + (j * page_data_len), page_data_len, 16, 1);
+ }
+ } else {
+ progress_runner(progress);
+ err = d_raw_to_fd(page_data, pages_chunk * page_data_len, o_fd);
+ if (err) {
+ fprintf(stderr, "%s: couldn't write all data to output file\n",
+ __func__);
+ goto end;
+ }
+ }
+ i += pages_chunk;
+ }
+ progress = 1.0f;
+ progress_runner(progress);
+ fprintf(stdout,"\n");
+ err = nvme_get_sct_status(fd, MASK_IGNORE);
+ if (err) {
+ fprintf(stderr, "%s: bad SCT status\n", __func__);
+ goto end;
+ }
+end:
+ if (o_fd >= 0) {
+ close(o_fd);
+ }
+ if (page_data) {
+ free(page_data);
+ }
+ return err;
+}
+
+static int nvme_get_internal_log_file(int fd, const char* const filename, bool current)
+{
+ int err;
+
+ /* Check device supported */
+ err = nvme_get_sct_status(fd, MASK_0 | MASK_1);
+ if (!err)
+ err = nvme_get_internal_log(fd, filename, current);
+ return err;
+}
+
+enum LOG_PAGE_C0 {
+ ERROR_LOG_C0 = 0,
+ SMART_HEALTH_LOG_C0,
+ FIRMWARE_SLOT_INFO_C0,
+ COMMAND_EFFECTS_C0,
+ DEVICE_SELF_TEST_C0,
+ LOG_PAGE_DIRECTORY_C0,
+ SMART_ATTRIBUTES_C0,
+ NR_SMART_ITEMS_C0,
+};
+
+struct nvme_xdn_smart_log_c0 {
+ __u8 items[NR_SMART_ITEMS_C0];
+ __u8 resv[512 - NR_SMART_ITEMS_C0];
+};
+
+static void default_show_vendor_log_c0(struct nvme_dev *dev, __u32 nsid,
+ struct nvme_xdn_smart_log_c0 *smart)
+{
+ printf("Vendor Log Page Directory 0xC0 for NVME device:%s namespace-id:%x\n",
+ dev->name, nsid);
+ printf("Error Log : %u \n", smart->items[ERROR_LOG_C0]);
+ printf("SMART Health Log : %u \n", smart->items[SMART_HEALTH_LOG_C0]);
+ printf("Firmware Slot Info : %u \n", smart->items[FIRMWARE_SLOT_INFO_C0]);
+ printf("Command Effects : %u \n", smart->items[COMMAND_EFFECTS_C0]);
+ printf("Device Self Test : %u \n", smart->items[DEVICE_SELF_TEST_C0]);
+ printf("Log Page Directory : %u \n", smart->items[LOG_PAGE_DIRECTORY_C0]);
+ printf("SMART Attributes : %u \n", smart->items[SMART_ATTRIBUTES_C0]);
+}
+
+static int nvme_get_vendor_log(struct nvme_dev *dev, __u32 namespace_id,
+ int log_page, const char* const filename)
+{
+ int err;
+ void* log = NULL;
+ size_t log_len = 512;
+
+ if (posix_memalign(&log, getpagesize(), log_len)) {
+ err = ENOMEM;
+ goto end;
+ }
+
+ /* Check device supported */
+ err = nvme_get_sct_status(dev_fd(dev), MASK_0 | MASK_1);
+ if (err) {
+ goto end;
+ }
+ err = nvme_get_nsid_log(dev_fd(dev), false, log_page, namespace_id,
+ log_len, log);
+ if (err) {
+ fprintf(stderr, "%s: couldn't get log 0x%x\n", __func__,
+ log_page);
+ goto end;
+ }
+ if (filename) {
+ int o_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+
+ if (o_fd < 0) {
+ fprintf(stderr, "%s: couldn't output file %s\n",
+ __func__, filename);
+ err = EINVAL;
+ goto end;
+ }
+ err = d_raw_to_fd(log, log_len, o_fd);
+ if (err) {
+ fprintf(stderr, "%s: couldn't write all data to output file %s\n",
+ __func__, filename);
+ /* Attempt following close */
+ }
+ if (close(o_fd)) {
+ err = errno;
+ goto end;
+ }
+ } else {
+ if (log_page == 0xc0)
+ default_show_vendor_log_c0(dev, namespace_id, log);
+ else
+ d(log, log_len,16,1);
+ }
+end:
+ if (log) {
+ free(log);
+ }
+ return err;
+}
+
+static int vendor_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ char *desc = "Get extended SMART information and show it.";
+ const char *namespace = "(optional) desired namespace";
+ const char *output_file = "(optional) binary output filename";
+ const char *log = "(optional) log ID (0xC0, or 0xCA), default 0xCA";
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ __u32 namespace_id;
+ const char* output_file;
+ int log;
+ };
+
+ struct config cfg = {
+ .namespace_id = 0xffffffff,
+ .output_file = NULL,
+ .log = 0xca
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
+ OPT_FILE("output-file", 'o', &cfg.output_file, output_file),
+ OPT_UINT("log", 'l', &cfg.log, log),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ fprintf(stderr,"%s: failed to parse arguments\n", __func__);
+ return EINVAL;
+ }
+
+ if ((cfg.log != 0xC0) && (cfg.log != 0xCA)) {
+ fprintf(stderr, "%s: invalid log page 0x%x - should be 0xC0 or 0xCA\n", __func__, cfg.log);
+ err = EINVAL;
+ goto end;
+ }
+
+ err = nvme_get_vendor_log(dev, cfg.namespace_id, cfg.log,
+ cfg.output_file);
+ if (err)
+ fprintf(stderr, "%s: couldn't get vendor log 0x%x\n", __func__, cfg.log);
+end:
+ if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
+
+static int internal_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ char *desc = "Get internal status log and show it.";
+ const char *output_file = "(optional) binary output filename";
+ const char *prev_log = "(optional) use previous log. Otherwise uses current log.";
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ const char* output_file;
+ bool prev_log;
+ };
+
+ struct config cfg = {
+ .output_file = NULL,
+ .prev_log = false
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FILE("output-file", 'o', &cfg.output_file, output_file),
+ OPT_FLAG("prev-log", 'p', &cfg.prev_log, prev_log),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ fprintf(stderr,"%s: failed to parse arguments\n", __func__);
+ return EINVAL;
+ }
+
+ if (cfg.prev_log)
+ printf("Getting previous log\n");
+ else
+ printf("Getting current log\n");
+
+ err = nvme_get_internal_log_file(dev_fd(dev), cfg.output_file,
+ !cfg.prev_log);
+ if (err < 0)
+ fprintf(stderr, "%s: couldn't get fw log \n", __func__);
+ if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
+
+static int clear_correctable_errors(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ char *desc = "Clear PCIe correctable error count.";
+ const __u32 namespace_id = 0xFFFFFFFF;
+ const __u32 feature_id = 0xCA;
+ const __u32 value = 1; /* Bit0 - reset clear PCIe correctable count */
+ const __u32 cdw12 = 0;
+ const bool save = false;
+ struct nvme_dev *dev;
+ __u32 result;
+ int err;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ fprintf(stderr,"%s: failed to parse arguments\n", __func__);
+ return EINVAL;
+ }
+
+ /* Check device supported */
+ err = nvme_get_sct_status(dev_fd(dev), MASK_0 | MASK_1);
+ if (err)
+ goto end;
+
+ struct nvme_set_features_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .fid = feature_id,
+ .nsid = namespace_id,
+ .cdw11 = value,
+ .cdw12 = cdw12,
+ .save = save,
+ .uuidx = 0,
+ .cdw15 = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+ err = nvme_set_features(&args);
+ if (err)
+ fprintf(stderr, "%s: couldn't clear PCIe correctable errors \n",
+ __func__);
+end:
+ if (err > 0)
+ nvme_show_status(err);
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/toshiba/toshiba-nvme.h b/plugins/toshiba/toshiba-nvme.h
new file mode 100644
index 0000000..6208f5d
--- /dev/null
+++ b/plugins/toshiba/toshiba-nvme.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/toshiba/toshiba-nvme
+
+#if !defined(TOSHIBA_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define TOSHIBA_NVME
+
+#include "cmd.h"
+#include "plugin.h"
+
+PLUGIN(NAME("toshiba", "Toshiba NVME plugin", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("vs-smart-add-log", "Extended SMART information", vendor_log)
+ ENTRY("vs-internal-log", "Get Internal Log", internal_log)
+ ENTRY("clear-pcie-correctable-errors", "Clear PCIe correctable error count", clear_correctable_errors)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/transcend/transcend-nvme.c b/plugins/transcend/transcend-nvme.c
new file mode 100644
index 0000000..024351f
--- /dev/null
+++ b/plugins/transcend/transcend-nvme.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+
+#define CREATE_CMD
+#include "transcend-nvme.h"
+
+static const __u32 OP_BAD_BLOCK = 0xc2;
+static const __u32 DW10_BAD_BLOCK = 0x400;
+static const __u32 DW12_BAD_BLOCK = 0x5a;
+
+static int getHealthValue(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_smart_log smart_log;
+ char *desc = "Get nvme health percentage.";
+ int percent_used = 0, healthvalue=0;
+ struct nvme_dev *dev;
+ int result;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ result = parse_and_open(&dev, argc, argv, desc, opts);
+ if (result) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+ result = nvme_get_log_smart(dev_fd(dev), 0xffffffff, false, &smart_log);
+ if (!result) {
+ printf("Transcend NVME heath value: ");
+ percent_used =smart_log.percent_used;
+
+ if(percent_used>100 || percent_used<0)
+ {
+ printf("0%%\n");
+ }
+ else
+ {
+ healthvalue = 100 - percent_used;
+ printf("%d%%\n",healthvalue);
+ }
+
+ }
+ dev_close(dev);
+ return result;
+}
+
+
+static int getBadblock(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+
+ char *desc = "Get nvme bad block number.";
+ struct nvme_dev *dev;
+ int result;
+
+ OPT_ARGS(opts) = {
+
+ OPT_END()
+ };
+
+ result = parse_and_open(&dev, argc, argv, desc, opts);
+ if (result) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+ unsigned char data[1]={0};
+ struct nvme_passthru_cmd nvmecmd;
+ memset(&nvmecmd,0,sizeof(nvmecmd));
+ nvmecmd.opcode=OP_BAD_BLOCK;
+ nvmecmd.cdw10=DW10_BAD_BLOCK;
+ nvmecmd.cdw12=DW12_BAD_BLOCK;
+ nvmecmd.addr = (__u64)(uintptr_t)data;
+ nvmecmd.data_len = 0x1;
+ result = nvme_submit_admin_passthru(dev_fd(dev), &nvmecmd, NULL);
+ if(!result) {
+ int badblock = data[0];
+ printf("Transcend NVME badblock count: %d\n",badblock);
+ }
+ dev_close(dev);
+ return result;
+}
diff --git a/plugins/transcend/transcend-nvme.h b/plugins/transcend/transcend-nvme.h
new file mode 100644
index 0000000..9c89883
--- /dev/null
+++ b/plugins/transcend/transcend-nvme.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/transcend/transcend-nvme
+
+#if !defined(TRANSCEND_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define TRANSCEND_NVME
+
+#include "cmd.h"
+
+
+PLUGIN(NAME("transcend", "Transcend vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("healthvalue", "NVME health percentage", getHealthValue)
+ ENTRY("badblock", "Get NVME bad block number", getBadblock)
+
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c
new file mode 100644
index 0000000..c8df126
--- /dev/null
+++ b/plugins/virtium/virtium-nvme.c
@@ -0,0 +1,1049 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <time.h>
+#include <locale.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "util/types.h"
+
+#define CREATE_CMD
+#include "virtium-nvme.h"
+
+#define MIN2(a, b) ( ((a) < (b))? (a) : (b))
+
+#define HOUR_IN_SECONDS 3600
+
+#define MAX_HEADER_BUFF (20 * 1024)
+#define MAX_LOG_BUFF 4096
+#define DEFAULT_TEST_NAME "Put the name of your test here"
+
+static char vt_default_log_file_name[256];
+
+struct vtview_log_header {
+ char path[256];
+ char test_name[256];
+ long int time_stamp;
+ struct nvme_id_ctrl raw_ctrl;
+ struct nvme_firmware_slot raw_fw;
+};
+
+struct vtview_smart_log_entry {
+ char path[256];
+ long int time_stamp;
+ struct nvme_id_ns raw_ns;
+ struct nvme_id_ctrl raw_ctrl;
+ struct nvme_smart_log raw_smart;
+};
+
+struct vtview_save_log_settings {
+ double run_time_hrs;
+ double log_record_frequency_hrs;
+ const char* output_file;
+ const char* test_name;
+};
+
+static void vt_initialize_header_buffer(struct vtview_log_header *pbuff)
+{
+ memset(pbuff->path, 0, sizeof(pbuff->path));
+ memset(pbuff->test_name, 0, sizeof(pbuff->test_name));
+}
+
+static void vt_convert_data_buffer_to_hex_string(const unsigned char *bufPtr,
+ const unsigned int size, const bool isReverted, char *output)
+{
+ unsigned int i, pos;
+ const char hextable[16] = {
+ '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'A', 'B',
+ 'C', 'D', 'E', 'F',
+ };
+
+ memset(output, 0, (size * 2) + 1);
+
+ for (i = 0; i < size; i++) {
+ if(isReverted)
+ pos = size - 1 - i;
+ else
+ pos = i;
+ output[2 * i] = hextable[(bufPtr[pos] & 0xF0) >> 4];
+ output[2 * i + 1] = hextable[(bufPtr[pos] & 0x0F)];
+ }
+}
+
+/*
+ * Generate log file name.
+ * Log file name will be generated automatically if user leave log file option blank.
+ * Log file name will be generated as vtView-Smart-log-date-time.txt
+ */
+static void vt_generate_vtview_log_file_name(char* fname)
+{
+ time_t current;
+ struct tm tstamp;
+ char temp[256];
+
+ time(&current);
+
+ tstamp = *localtime(&current);
+ snprintf(temp, sizeof(temp), "./vtView-Smart-log-");
+ strcat(fname, temp);
+ strftime(temp, sizeof(temp), "%Y-%m-%d", &tstamp);
+ strcat(fname, temp);
+ snprintf(temp, sizeof(temp), ".txt");
+ strcat(fname, temp);
+}
+
+static void vt_convert_smart_data_to_human_readable_format(struct vtview_smart_log_entry *smart, char *text)
+{
+ char tempbuff[1024] = "";
+ int i;
+ int temperature = ((smart->raw_smart.temperature[1] << 8) | smart->raw_smart.temperature[0]) - 273;
+ double capacity;
+ char *curlocale;
+ char *templocale;
+ __u8 lba_index;
+ nvme_id_ns_flbas_to_lbaf_inuse(smart->raw_ns.flbas, &lba_index);
+
+ curlocale = setlocale(LC_ALL, NULL);
+ templocale = strdup(curlocale);
+
+ if (NULL == templocale)
+ printf("Cannot malloc buffer\n");
+
+ setlocale(LC_ALL, "C");
+
+ unsigned long long int lba = 1ULL << smart->raw_ns.lbaf[lba_index].ds;
+ capacity = le64_to_cpu(smart->raw_ns.nsze) * lba;
+
+ snprintf(tempbuff, sizeof(tempbuff), "log;%s;%lu;%s;%s;%-.*s;", smart->raw_ctrl.sn, smart->time_stamp, smart->path,
+ smart->raw_ctrl.mn, (int)sizeof(smart->raw_ctrl.fr), smart->raw_ctrl.fr);
+ strcpy(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Capacity;%lf;", capacity / 1000000000);
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Critical_Warning;%u;", smart->raw_smart.critical_warning);
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Temperature;%u;", temperature);
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Available_Spare;%u;", smart->raw_smart.avail_spare);
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Available_Spare_Threshold;%u;", smart->raw_smart.spare_thresh);
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Percentage_Used;%u;", smart->raw_smart.percent_used);
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Data_Units_Read;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.data_units_read)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Data_Units_Written;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.data_units_written)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Host_Read_Commands;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.host_reads)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Host_Write_Commands;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.host_writes)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Controller_Busy_Time;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.ctrl_busy_time)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Power_Cycles;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.power_cycles)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Power_On_Hours;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.power_on_hours)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Unsafe_Shutdowns;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.unsafe_shutdowns)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Media_Errors;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.media_errors)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Num_Err_Log_Entries;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.num_err_log_entries)));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Warning_Temperature_Time;%u;", le32_to_cpu(smart->raw_smart.warning_temp_time));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Critical_Composite_Temperature_Time;%u;", le32_to_cpu(smart->raw_smart.critical_comp_time));
+ strcat(text, tempbuff);
+
+ for (i = 0; i < 8; i++) {
+ __s32 temp = le16_to_cpu(smart->raw_smart.temp_sensor[i]);
+ if (0 == temp) {
+ snprintf(tempbuff, sizeof(tempbuff), "Temperature_Sensor_%d;NC;", i);
+ strcat(text, tempbuff);
+ continue;
+ }
+ snprintf(tempbuff, sizeof(tempbuff), "Temperature_Sensor_%d;%d;", i, temp - 273);
+ strcat(text, tempbuff);
+ }
+
+ snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T1_Trans_Count;%u;", le32_to_cpu(smart->raw_smart.thm_temp1_trans_count));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T2_Trans_Count;%u;", le32_to_cpu(smart->raw_smart.thm_temp2_trans_count));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T1_Total_Time;%u;", le32_to_cpu(smart->raw_smart.thm_temp1_total_time));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Thermal_Management_T2_Total_Time;%u;", le32_to_cpu(smart->raw_smart.thm_temp2_total_time));
+ strcat(text, tempbuff);
+
+ snprintf(tempbuff, sizeof(tempbuff), "NandWrites;%d;\n", 0);
+ strcat(text, tempbuff);
+
+ setlocale(LC_ALL, templocale);
+ free(templocale);
+}
+
+static void vt_header_to_string(const struct vtview_log_header *header, char *text)
+{
+ char timebuff[50] = "";
+ char tempbuff[MAX_HEADER_BUFF] = "";
+ char identext[16384] = "";
+ char fwtext[2048] = "";
+
+ strftime(timebuff, 50, "%Y-%m-%d %H:%M:%S", localtime(&(header->time_stamp)));
+ snprintf(tempbuff, MAX_HEADER_BUFF, "header;{\"session\":{\"testName\":\"%s\",\"dateTime\":\"%s\"},",
+ header->test_name, timebuff);
+ strcpy(text, tempbuff);
+
+ vt_convert_data_buffer_to_hex_string((unsigned char *)&(header->raw_ctrl), sizeof(header->raw_ctrl), false, identext);
+ vt_convert_data_buffer_to_hex_string((unsigned char *)&(header->raw_fw), sizeof(header->raw_fw), false, fwtext);
+ snprintf(tempbuff, MAX_HEADER_BUFF,
+ "\"devices\":[{\"model\":\"%s\",\"port\":\"%s\",\"SN\":\"%s\",\"type\":\"NVMe\",\"identify\":\"%s\",\"firmwareSlot\":\"%s\"}]}\n",
+ header->raw_ctrl.mn, header->path, header->raw_ctrl.sn, identext, fwtext);
+ strcat(text, tempbuff);
+}
+
+static int vt_append_text_file(const char *text, const char *filename)
+{
+ FILE *f;
+
+ f = fopen(filename, "a");
+ if(NULL == f) {
+ printf("Cannot open %s\n", filename);
+ return -1;
+ }
+
+ fprintf(f, "%s", text);
+ fclose(f);
+ return 0;
+}
+
+static int vt_append_log(struct vtview_smart_log_entry *smart, const char *filename)
+{
+ char sm_log_text[MAX_LOG_BUFF] = "";
+
+ vt_convert_smart_data_to_human_readable_format(smart, sm_log_text);
+ return vt_append_text_file(sm_log_text, filename);
+}
+
+static int vt_append_header(const struct vtview_log_header *header, const char *filename)
+{
+ char header_text[MAX_HEADER_BUFF] = "";
+
+ vt_header_to_string(header, header_text);
+ return vt_append_text_file(header_text, filename);
+}
+
+static void vt_process_string(char *str, const size_t size)
+{
+ size_t i;
+
+ if (size == 0)
+ return;
+
+ i = size - 1;
+ while ((0 != i) && (' ' == str[i])) {
+ str[i] = 0;
+ i--;
+ }
+}
+
+static int vt_add_entry_to_log(const int fd, const char *path, const struct vtview_save_log_settings *cfg)
+{
+ struct vtview_smart_log_entry smart;
+ const char *filename;
+ int ret = 0;
+ unsigned nsid = 0;
+
+ memset(smart.path, 0, sizeof(smart.path));
+ strncpy(smart.path, path, sizeof(smart.path) - 1);
+ if(NULL == cfg->output_file)
+ filename = vt_default_log_file_name;
+ else
+ filename = cfg->output_file;
+
+ smart.time_stamp = time(NULL);
+ ret = nvme_get_nsid(fd, &nsid);
+
+ if (ret < 0) {
+ printf("Cannot read namespace-id\n");
+ return -1;
+ }
+
+ ret = nvme_identify_ns(fd, nsid, &smart.raw_ns);
+ if (ret) {
+ printf("Cannot read namespace identify\n");
+ return -1;
+ }
+
+ ret = nvme_identify_ctrl(fd, &smart.raw_ctrl);
+ if (ret) {
+ printf("Cannot read device identify controller\n");
+ return -1;
+ }
+
+ ret = nvme_get_log_smart(fd, NVME_NSID_ALL, false, &smart.raw_smart);
+ if (ret) {
+ printf("Cannot read device SMART log\n");
+ return -1;
+ }
+
+ vt_process_string(smart.raw_ctrl.sn, sizeof(smart.raw_ctrl.sn));
+ vt_process_string(smart.raw_ctrl.mn, sizeof(smart.raw_ctrl.mn));
+
+ ret = vt_append_log(&smart, filename);
+ return (ret);
+}
+
+static int vt_update_vtview_log_header(const int fd, const char *path, const struct vtview_save_log_settings *cfg)
+{
+ struct vtview_log_header header;
+ const char *filename;
+ int ret = 0;
+
+ vt_initialize_header_buffer(&header);
+ if (strlen(path) > sizeof(header.path)) {
+ printf("filename too long\n");
+ errno = EINVAL;
+ return -1;
+ }
+ strcpy(header.path, path);
+
+ if (NULL == cfg->test_name)
+ strcpy(header.test_name, DEFAULT_TEST_NAME);
+ else {
+ if (strlen(cfg->test_name) > sizeof(header.test_name)) {
+ printf("test name too long\n");
+ errno = EINVAL;
+ return -1;
+ }
+ strcpy(header.test_name, cfg->test_name);
+ }
+
+ if(NULL == cfg->output_file)
+ filename = vt_default_log_file_name;
+ else
+ filename = cfg->output_file;
+
+ printf("Log file: %s\n", filename);
+ header.time_stamp = time(NULL);
+
+ ret = nvme_identify_ctrl(fd, &header.raw_ctrl);
+ if (ret) {
+ printf("Cannot read identify device\n");
+ return -1;
+ }
+
+ ret = nvme_get_log_fw_slot(fd, false, &header.raw_fw);
+ if (ret) {
+ printf("Cannot read device firmware log\n");
+ return -1;
+ }
+
+ vt_process_string(header.raw_ctrl.sn, sizeof(header.raw_ctrl.sn));
+ vt_process_string(header.raw_ctrl.mn, sizeof(header.raw_ctrl.mn));
+
+ ret = vt_append_header(&header, filename);
+ return (ret);
+}
+
+static void vt_build_identify_lv2(unsigned int data, unsigned int start,
+ unsigned int count, const char **table,
+ bool isEnd)
+{
+ unsigned int i, end, pos, sh = 1;
+ unsigned int temp;
+
+ end = start + count;
+
+ for (i = start; i < end; i++) {
+ temp = ((data & (sh << i)) >> i);
+ pos = i * 2;
+ printf(" \"bit %u\":\"%ub %s\"\n", i, temp, table[pos]);
+ printf(" %s", table[pos + 1]);
+
+ if((end - 1) != i || !isEnd)
+ printf(",\n");
+ else
+ printf("\n");
+ }
+
+ if(isEnd)
+ printf(" },\n");
+}
+
+static void vt_build_power_state_descriptor(const struct nvme_id_ctrl *ctrl)
+{
+ unsigned int i;
+ unsigned char *buf;
+
+ printf("{\n");
+ printf("\"Power State Descriptors\":{\n");
+ printf(" \"NOPS\":\"Non-Operational State,\"\n");
+ printf(" \"MPS\":\"Max Power Scale (0: in 0.01 Watts; 1: in 0.0001 Watts),\"\n");
+ printf(" \"ENLAT\":\"Entry Latency in microseconds,\"\n");
+ printf(" \"RWL\":\"Relative Write Latency,\"\n");
+ printf(" \"RRL\":\"Relative Read Latency,\"\n");
+ printf(" \"IPS\":\"Idle Power Scale (00b: Not reported; 01b: 0.0001 W; 10b: 0.01 W; 11b: Reserved),\"\n");
+ printf(" \"APS\":\"Active Power Scale (00b: Not reported; 01b: 0.0001 W; 10b: 0.01 W; 11b: Reserved),\"\n");
+ printf(" \"ACTP\":\"Active Power,\"\n");
+ printf(" \"MP\":\"Maximum Power,\"\n");
+ printf(" \"EXLAT\":\"Exit Latency in microsecond,\"\n");
+ printf(" \"RWT\":\"Relative Write Throughput,\"\n");
+ printf(" \"RRT\":\"Relative Read Throughput,\"\n");
+ printf(" \"IDLP\":\"Idle Power,\"\n");
+ printf(" \"APW\":\"Active Power Workload,\"\n");
+ printf(" \"Ofs\":\"BYTE Offset,\"\n");
+
+ printf(" \"Power State Descriptors\":\"\n");
+
+ printf("%6s%10s%5s%4s%6s%10s%10s%10s%4s%4s%4s%4s%10s%4s%6s%10s%4s%5s%6s\n", "Entry", "0fs 00-03", "NOPS", "MPS", "MP", "ENLAT", "EXLAT", "0fs 12-15",
+ "RWL", "RWT", "RRL", "RRT", "0fs 16-19", "IPS", "IDLP", "0fs 20-23", "APS", "APW", "ACTP");
+
+
+ printf("%6s%10s%5s%4s%6s%10s%10s%10s%4s%4s%4s%4s%10s%4s%6s%10s%4s%5s%6s\n", "=====", "=========", "====", "===", "=====", "=========", "=========",
+ "=========", "===", "===", "===", "===", "=========", "===", "=====", "=========", "===", "====", "=====");
+
+ for (i = 0; i < 32; i++) {
+ char s[100];
+ unsigned int temp;
+
+ printf("%6d", i);
+ buf = (unsigned char*) (&ctrl->psd[i]);
+ vt_convert_data_buffer_to_hex_string(&buf[0], 4, true, s);
+ printf("%9sh", s);
+
+ temp = ctrl->psd[i].flags;
+ printf("%4ub", ((unsigned char)temp & 0x02));
+ printf("%3ub", ((unsigned char)temp & 0x01));
+ vt_convert_data_buffer_to_hex_string(&buf[0], 2, true, s);
+ printf("%5sh", s);
+
+ vt_convert_data_buffer_to_hex_string(&buf[4], 4, true, s);
+ printf("%9sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[8], 4, true, s);
+ printf("%9sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[12], 4, true, s);
+ printf("%9sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[15], 1, true, s);
+ printf("%3sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[14], 1, true, s);
+ printf("%3sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[13], 1, true, s);
+ printf("%3sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[12], 1, true, s);
+ printf("%3sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[16], 4, true, s);
+ printf("%9sh", s);
+
+ temp = ctrl->psd[i].ips;
+ snprintf(s, sizeof(s), "%u%u", (((unsigned char)temp >> 6) & 0x01), (((unsigned char)temp >> 7) & 0x01));
+ printf("%3sb", s);
+
+ vt_convert_data_buffer_to_hex_string(&buf[16], 2, true, s);
+ printf("%5sh", s);
+ vt_convert_data_buffer_to_hex_string(&buf[20], 4, true, s);
+ printf("%9sh", s);
+
+ temp = ctrl->psd[i].apws;
+ snprintf(s, sizeof(s), "%u%u", (((unsigned char)temp >> 6) & 0x01), (((unsigned char)temp >> 7) & 0x01));
+ printf("%3sb", s);
+ snprintf(s, sizeof(s), "%u%u%u", (((unsigned char)temp) & 0x01), (((unsigned char)temp >> 1) & 0x01), (((unsigned char)temp >> 2) & 0x01));
+ printf("%4sb", s);
+
+ vt_convert_data_buffer_to_hex_string(&buf[20], 2, true, s);
+ printf("%5sh", s);
+ printf("\n");
+ }
+
+ printf(" \"}\n}\n");
+
+}
+
+static void vt_dump_hex_data(const unsigned char *pbuff, size_t pbuffsize) {
+
+ char textbuf[33];
+ unsigned long int i, j;
+
+ textbuf[32] = '\0';
+ printf("[%08X] ", 0);
+ for (i = 0; i < pbuffsize; i++) {
+ printf("%02X ", pbuff[i]);
+
+ if (pbuff[i] >= ' ' && pbuff[i] <= '~')
+ textbuf[i % 32] = pbuff[i];
+ else
+ textbuf[i % 32] = '.';
+
+ if ((((i + 1) % 8) == 0) || ((i + 1) == pbuffsize)) {
+ printf(" ");
+ if ((i + 1) % 32 == 0) {
+ printf(" %s\n", textbuf);
+ if((i + 1) != pbuffsize)
+ printf("[%08lX] ", (i + 1));
+ }
+ else if (i + 1 == pbuffsize) {
+ textbuf[(i + 1) % 32] = '\0';
+ if(((i + 1) % 8) == 0)
+ printf(" ");
+
+ for (j = ((i + 1) % 32); j < 32; j++) {
+ printf(" ");
+ if(((j + 1) % 8) == 0)
+ printf(" ");
+ }
+
+ printf("%s\n", textbuf);
+ }
+ }
+ }
+}
+
+static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl)
+{
+ unsigned char *buf;
+ unsigned int temp, pos;
+ char s[1024] = "";
+
+ const char *CMICtable[6] = {"0 = the NVM subsystem contains only a single NVM subsystem port",
+ "1 = the NVM subsystem may contain more than one subsystem ports",
+ "0 = the NVM subsystem contains only a single controller",
+ "1 = the NVM subsystem may contain two or more controllers (see section 1.4.1)",
+ "0 = the controller is associated with a PCI Function or a Fabrics connection",
+ "1 = the controller is associated with an SR-IOV Virtual Function"};
+
+ const char *OAEStable[20] = {"Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "0 = does not support sending the Namespace Attribute Notices event nor the associated Changed Namespace List log page",
+ "1 = supports sending the Namespace Attribute Notices & the associated Changed Namespace List log page",
+ "0 = does not support sending Firmware Activation Notices event",
+ "1 = supports sending Firmware Activation Notices"};
+
+ const char *CTRATTtable[4] = {"0 = does not support a 128-bit Host Identifier",
+ "1 = supports a 128-bit Host Identifier",
+ "0 = does not support Non-Operational Power State Permissive Mode",
+ "1 = supports Non-Operational Power State Permissive Mode"};
+
+ const char *OACStable[18] = {"0 = does not support the Security Send and Security Receive commands",
+ "1 = supports the Security Send and Security Receive commands",
+ "0 = does not support the Format NVM command",
+ "1 = supports the Format NVM command",
+ "0 = does not support the Firmware Commit and Firmware Image Download commands",
+ "1 = supports the Firmware Commit and Firmware Image Download commands",
+ "0 = does not support the Namespace Management capability",
+ "1 = supports the Namespace Management capability",
+ "0 = does not support the Device Self-test command",
+ "1 = supports the Device Self-test command",
+ "0 = does not support Directives",
+ "1 = supports Directive Send & Directive Receive commands",
+ "0 = does not support the NVMe-MI Send and NVMe-MI Receive commands",
+ "1 = supports the NVMe-MI Send and NVMe-MI Receive commands",
+ "0 = does not support the Virtualization Management command",
+ "1 = supports the Virtualization Management command",
+ "0 = does not support the Doorbell Buffer Config command",
+ "1 = supports the Doorbell Buffer Config command"};
+
+ const char *FRMWtable[10] = {"0 = the 1st firmware slot (slot 1) is read/write",
+ "1 = the 1st firmware slot (slot 1) is read only",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "0 = requires a reset for firmware to be activated",
+ "1 = supports firmware activation without a reset"};
+
+ const char *LPAtable[8] = {"0 = does not support the SMART / Health information log page on a per namespace basis",
+ "1 = supports the SMART / Health information log page on a per namespace basis",
+ "0 = does not support the Commands Supported & Effects log page",
+ "1 = supports the Commands Supported Effects log page",
+ "0 = does not support extended data for Get Log Page",
+ "1 = supports extended data for Get Log Page (including extended Number of Dwords and Log Page Offset fields)",
+ "0 = does not support the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and Telemetry Log Notices events",
+ "1 = supports the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and sending Telemetry Log Notices" };
+
+ const char *AVSCCtable[2] = {"0 = the format of all Admin Vendor Specific Commands are vendor specific",
+ "1 = all Admin Vendor Specific Commands use the format defined in NVM Express specification"};
+
+ const char *APSTAtable[2] = {"0 = does not support autonomous power state transitions",
+ "1 = supports autonomous power state transitions"};
+
+ const char *DSTOtable[2] = {"0 = the NVM subsystem supports one device self-test operation per controller at a time",
+ "1 = the NVM subsystem supports only one device self-test operation in progress at a time"};
+
+ const char *HCTMAtable[2] = {"0 = does not support host controlled thermal management",
+ "1 = supports host controlled thermal management. Supports Set Features & Get Features commands with the Feature Identifier field set to 10h"};
+
+ const char *SANICAPtable[6] = {"0 = does not support the Crypto Erase sanitize operation",
+ "1 = supports the Crypto Erase sanitize operation",
+ "0 = does not support the Block Erase sanitize operation",
+ "1 = supports the Block Erase sanitize operation",
+ "0 = does not support the Overwrite sanitize operation",
+ "1 = supports the Overwrite sanitize operation"};
+
+ const char *ONCStable[14] = {"0 = does not support the Compare command",
+ "1 = supports the Compare command",
+ "0 = does not support the Write Uncorrectable command",
+ "1 = supports the Write Uncorrectable command",
+ "0 = does not support the Dataset Management command",
+ "1 = supports the Dataset Management command",
+ "0 = does not support the Write Zeroes command",
+ "1 = supports the Write Zeroes command",
+ "0 = does not support the Save field set to a non-zero value in the Set Features and the Get Features commands",
+ "1 = supports the Save field set to a non-zero value in the Set Features and the Get Features commands", \
+ "0 = does not support reservations",
+ "1 = supports reservations",
+ "0 = does not support the Timestamp feature (refer to section 5.21.1.14)",
+ "1 = supports the Timestamp feature"};
+
+ const char *FUSEStable[2] = {"0 = does not support the Compare and Write fused operation",
+ "1 = supports the Compare and Write fused operation"};
+
+ const char *FNAtable[6] = {"0 = supports format on a per namespace basis",
+ "1 = all namespaces shall be configured with the same attributes and a format (excluding secure erase) of any namespace results in a format of all namespaces in an NVM subsystem",
+ "0 = any secure erase performed as part of a format results in a secure erase of a particular namespace specified",
+ "1 = any secure erase performed as part of a format operation results in a secure erase of all namespaces in the NVM subsystem",
+ "0 = cryptographic erase is not supported",
+ "1 = cryptographic erase is supported as part of the secure erase functionality"};
+
+ const char *VWCtable[2] = {"0 = a volatile write cache is not present",
+ "1 = a volatile write cache is present"};
+
+ 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",
+ "01b = SGLs are supported. There is no alignment nor granularity requirement for Data Blocks",
+ "10b = SGLs are supported. There is a Dword alignment and granularity requirement for Data Blocks",
+ "11b = Reserved"};
+
+ const char *SGLStable[42] = {"Used",
+ "Used",
+ "Used",
+ "Used",
+ "0 = does not support the Keyed SGL Data Block descriptor",
+ "1 = supports the Keyed SGL Data Block descriptor",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "0 = the SGL Bit Bucket descriptor is not supported",
+ "1 = the SGL Bit Bucket descriptor is supported",
+ "0 = use of a byte aligned contiguous physical buffer of metadata is not supported",
+ "1 = use of a byte aligned contiguous physical buffer of metadata is supported",
+ "0 = the SGL length shall be equal to the amount of data to be transferred",
+ "1 = supports commands that contain a data or metadata SGL of a length larger than the amount of data to be transferred",
+ "0 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is not supported",
+ "1 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is supported",
+ "0 = the Address field specifying an offset is not supported",
+ "1 = supports the Address field in SGL Data Block, SGL Segment, and SGL Last Segment descriptor types specifying an offset"};
+
+ buf = (unsigned char *)(ctrl);
+
+ printf("{\n");
+ vt_convert_data_buffer_to_hex_string(buf, 2, true, s);
+ printf(" \"PCI Vendor ID\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[2], 2, true, s);
+ printf(" \"PCI Subsystem Vendor ID\":\"%sh\",\n", s);
+ printf(" \"Serial Number\":\"%s\",\n", ctrl->sn);
+ printf(" \"Model Number\":\"%s\",\n", ctrl->mn);
+ printf(" \"Firmware Revision\":\"%-.*s\",\n", (int)sizeof(ctrl->fr), ctrl->fr);
+ vt_convert_data_buffer_to_hex_string(&buf[72], 1, true, s);
+ printf(" \"Recommended Arbitration Burst\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[73], 3, true, s);
+ printf(" \"IEEE OUI Identifier\":\"%sh\",\n", s);
+
+ temp = ctrl->cmic;
+ printf(" \"Controller Multi-Path I/O and Namespace Sharing Capabilities\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[76], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 3, CMICtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[77], 1, true, s);
+ printf(" \"Maximum Data Transfer Size\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[78], 2, true, s);
+ printf(" \"Controller ID\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[80], 4, true, s);
+ printf(" \"Version\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[84], 4, true, s);
+ printf(" \"RTD3 Resume Latency\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[88], 4, true, s);
+ printf(" \"RTD3 Entry Latency\":\"%sh\",\n", s);
+
+ temp = le32_to_cpu(ctrl->oaes);
+ printf(" \"Optional Asynchronous Events Supported\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[92], 4, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 8, 2, OAEStable, true);
+
+ temp = le32_to_cpu(ctrl->ctratt);
+ printf(" \"Controller Attributes\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[96], 4, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 2, CTRATTtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[122], 16, true, s);
+ printf(" \"FRU Globally Unique Identifier\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[240], 16, true, s);
+ printf(" \"NVMe Management Interface Specification\":\"%sh\",\n", s);
+
+ temp = le16_to_cpu(ctrl->oacs);
+ printf(" \"Optional Admin Command Support\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[256], 2, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 9, OACStable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[258], 1, true, s);
+ printf(" \"Abort Command Limit\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[259], 1, true, s);
+ printf(" \"Asynchronous Event Request Limit\":\"%sh\",\n", s);
+
+ temp = ctrl->frmw;
+ printf(" \"Firmware Updates\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[260], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, FRMWtable, false);
+ vt_convert_data_buffer_to_hex_string(&buf[260], 1, true, s);
+ printf(" \"Firmware Slot\":\"%uh\",\n", ((ctrl->frmw >> 1) & 0x07));
+ vt_build_identify_lv2(temp, 4, 1, FRMWtable, true);
+
+ temp = ctrl->lpa;
+ printf(" \"Log Page Attributes\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[261], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 4, LPAtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[262], 1, true, s);
+ printf(" \"Error Log Page Entries\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[263], 1, true, s);
+ printf(" \"Number of Power States Support\":\"%sh\",\n", s);
+
+ temp = ctrl->avscc;
+ printf(" \"Admin Vendor Specific Command Configuration\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[264], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, AVSCCtable, true);
+
+ temp = ctrl->apsta;
+ printf(" \"Autonomous Power State Transition Attributes\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[265], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, APSTAtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[266], 2, true, s);
+ printf(" \"Warning Composite Temperature Threshold\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[268], 2, true, s);
+ printf(" \"Critical Composite Temperature Threshold\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[270], 2, true, s);
+ printf(" \"Maximum Time for Firmware Activation\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[272], 4, true, s);
+ printf(" \"Host Memory Buffer Preferred Size\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[276], 4, true, s);
+ printf(" \"Host Memory Buffer Minimum Size\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[280], 16, true, s);
+ printf(" \"Total NVM Capacity\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[296], 16, true, s);
+ printf(" \"Unallocated NVM Capacity\":\"%sh\",\n", s);
+
+ temp = le32_to_cpu(ctrl->rpmbs);
+ printf(" \"Replay Protected Memory Block Support\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[312], 4, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ printf(" \"Number of RPMB Units\":\"%u\",\n", (temp & 0x00000003));
+ snprintf(s, sizeof(s), ((temp >> 3) & 0x00000007)? "Reserved" : "HMAC SHA-256");
+ printf(" \"Authentication Method\":\"%u: %s\",\n", ((temp >> 3) & 0x00000007), s);
+ printf(" \"Total Size\":\"%u\",\n", ((temp >> 16) & 0x000000FF));
+ printf(" \"Access Size\":\"%u\",\n", ((temp >> 24) & 0x000000FF));
+ printf(" },\n");
+
+ vt_convert_data_buffer_to_hex_string(&buf[316], 2, true, s);
+ printf(" \"Extended Device Self-test Time\":\"%sh\",\n", s);
+
+ temp = ctrl->dsto;
+ printf(" \"Device Self-test Options\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[318], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, DSTOtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[319], 1, true, s);
+ printf(" \"Firmware Update Granularity\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[320], 1, true, s);
+ printf(" \"Keep Alive Support\":\"%sh\",\n", s);
+
+ temp = le16_to_cpu(ctrl->hctma);
+ printf(" \"Host Controlled Thermal Management Attributes\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[322], 2, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, HCTMAtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[324], 2, true, s);
+ printf(" \"Minimum Thermal Management Temperature\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[326], 2, true, s);
+ printf(" \"Maximum Thermal Management Temperature\":\"%sh\",\n", s);
+
+ temp = le16_to_cpu(ctrl->sanicap);
+ printf(" \"Sanitize Capabilities\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[328], 2, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 3, SANICAPtable, true);
+
+ temp = ctrl->sqes;
+ printf(" \"Submission Queue Entry Size\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[512], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ printf(" \"Maximum Size\":\"%u\",\n", (temp & 0x0000000F));
+ printf(" \"Required Size\":\"%u\",\n", ((temp >> 4) & 0x0000000F));
+ printf(" }\n");
+
+ temp = ctrl->cqes;
+ printf(" \"Completion Queue Entry Size\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[513], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ printf(" \"Maximum Size\":\"%u\",\n", (temp & 0x0000000F));
+ printf(" \"Required Size\":\"%u\",\n", ((temp >> 4) & 0x0000000F));
+ printf(" }\n");
+
+ vt_convert_data_buffer_to_hex_string(&buf[514], 2, true, s);
+ printf(" \"Maximum Outstanding Commands\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[516], 4, true, s);
+ printf(" \"Number of Namespaces\":\"%sh\",\n", s);
+
+ temp = le16_to_cpu(ctrl->oncs);
+ printf(" \"Optional NVM Command Support\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[520], 2, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 7, ONCStable, true);
+
+ temp = le16_to_cpu(ctrl->fuses);
+ printf(" \"Fused Operation Support\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[522], 2, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, FUSEStable, true);
+
+ temp = ctrl->fna;
+ printf(" \"Format NVM Attributes\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[524], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 3, FNAtable, true);
+
+ temp = ctrl->vwc;
+ printf(" \"Volatile Write Cache\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[525], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, VWCtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[526], 2, true, s);
+ printf(" \"Atomic Write Unit Normal\":\"%sh\",\n", s);
+ vt_convert_data_buffer_to_hex_string(&buf[528], 2, true, s);
+ printf(" \"Atomic Write Unit Power Fail\":\"%sh\",\n", s);
+
+ 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, ICSVSCCtable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[532], 2, true, s);
+ printf(" \"Atomic Compare 0 Write Unit\":\"%sh\",\n", s);
+
+ temp = le32_to_cpu(ctrl->sgls);
+ printf(" \"SGL Support\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[536], 4, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ pos = (temp & 0x00000003);
+ printf(" \"bit 1:0\":\"%s\",\n", SGLSSubtable[pos]);
+ vt_build_identify_lv2(temp, 2, 1, SGLStable, false);
+ vt_build_identify_lv2(temp, 16, 5, SGLStable, true);
+
+ vt_convert_data_buffer_to_hex_string(&buf[768], 256, false, s);
+ printf(" \"NVM Subsystem NVMe Qualified Name\":\"%s\",\n", s);
+ printf("}\n\n");
+
+ vt_build_power_state_descriptor(ctrl);
+
+
+ printf("\n{\n");
+ printf("\"Vendor Specific\":\"\n");
+ vt_dump_hex_data(&buf[3072], 1024);
+ printf("\"}\n");
+}
+
+static int vt_save_smart_to_vtview_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int ret, err = 0;
+ long int total_time = 0;
+ long int freq_time = 0;
+ long int cur_time = 0;
+ long int remain_time = 0;
+ long int start_time = 0;
+ long int end_time = 0;
+ char path[256] = "";
+ char *desc = "Save SMART data into log file with format that is easy to analyze (comma delimited). Maximum log file will be 4K.\n\n"
+ "Typical usages:\n\n"
+ "Temperature characterization: \n"
+ "\tvirtium save-smart-to-vtview-log /dev/yourDevice --run-time=100 --record-frequency=0.25 --test-name=burn-in-at-(-40)\n\n"
+ "Endurance testing : \n"
+ "\tvirtium save-smart-to-vtview-log /dev/yourDevice --run-time=100 --record-frequency=1 --test-name=Endurance-test-JEDEG-219-workload\n\n"
+ "Just logging :\n"
+ "\tvirtium save-smart-to-vtview-log /dev/yourDevice";
+
+ const char *run_time = "(optional) Number of hours to log data (default = 20 hours)";
+ const char *freq = "(optional) How often you want to log SMART data (0.25 = 15' , 0.5 = 30' , 1 = 1 hour, 2 = 2 hours, etc.). Default = 10 hours.";
+ const char *output_file = "(optional) Name of the log file (give it a name that easy for you to remember what the test is). You can leave it blank too, we will take care it for you.";
+ const char *test_name = "(optional) Name of the test you are doing. We use this as part of the name of the log file.";
+ struct nvme_dev *dev;
+
+ struct vtview_save_log_settings cfg = {
+ .run_time_hrs = 20,
+ .log_record_frequency_hrs = 10,
+ .output_file = NULL,
+ .test_name = NULL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_DOUBLE("run-time", 'r', &cfg.run_time_hrs, run_time),
+ OPT_DOUBLE("freq", 'f', &cfg.log_record_frequency_hrs, freq),
+ OPT_FILE("output-file", 'o', &cfg.output_file, output_file),
+ OPT_STRING("test-name", 'n', "NAME", &cfg.test_name, test_name),
+ OPT_END()
+ };
+
+ vt_generate_vtview_log_file_name(vt_default_log_file_name);
+
+ if (argc >= 2) {
+ if (strlen(argv[1]) > sizeof(path) - 1) {
+ printf("Filename too long\n");
+ return -1;
+ }
+ strcpy(path, argv[1]);
+ }
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf("Error parse and open (err = %d)\n", err);
+ return err;
+ }
+
+ printf("Running...\n");
+ printf("Collecting data for device %s\n", path);
+ printf("Running for %lf hour(s)\n", cfg.run_time_hrs);
+ printf("Logging SMART data for every %lf hour(s)\n", cfg.log_record_frequency_hrs);
+
+ ret = vt_update_vtview_log_header(dev_fd(dev), path, &cfg);
+ if (ret) {
+ err = EINVAL;
+ dev_close(dev);
+ return (err);
+ }
+
+ total_time = cfg.run_time_hrs * (float)HOUR_IN_SECONDS;
+ freq_time = cfg.log_record_frequency_hrs * (float)HOUR_IN_SECONDS;
+
+ if(freq_time == 0)
+ freq_time = 1;
+
+ start_time = time(NULL);
+ end_time = start_time + total_time;
+
+ fflush(stdout);
+
+ while (1) {
+ cur_time = time(NULL);
+ if(cur_time >= end_time)
+ break;
+
+ ret = vt_add_entry_to_log(dev_fd(dev), path, &cfg);
+ if (ret) {
+ printf("Cannot update driver log\n");
+ break;
+ }
+
+ remain_time = end_time - cur_time;
+ freq_time = MIN2(freq_time, remain_time);
+ sleep(freq_time);
+ fflush(stdout);
+ }
+
+ dev_close(dev);
+ return (err);
+}
+
+static int vt_show_identify(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int ret, err = 0;
+ struct nvme_id_ctrl ctrl;
+ struct nvme_dev *dev;
+ char *desc = "Parse identify data to json format\n\n"
+ "Typical usages:\n\n"
+ "virtium show-identify /dev/yourDevice\n";
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err) {
+ printf("Error parse and open (err = %d)\n", err);
+ return err;
+ }
+
+ ret = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (ret) {
+ printf("Cannot read identify device\n");
+ dev_close(dev);
+ return (-1);
+ }
+
+ vt_process_string(ctrl.sn, sizeof(ctrl.sn));
+ vt_process_string(ctrl.mn, sizeof(ctrl.mn));
+ vt_parse_detail_identify(&ctrl);
+
+ dev_close(dev);
+ return (err);
+}
diff --git a/plugins/virtium/virtium-nvme.h b/plugins/virtium/virtium-nvme.h
new file mode 100644
index 0000000..0a04a35
--- /dev/null
+++ b/plugins/virtium/virtium-nvme.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/virtium/virtium-nvme
+
+#if !defined(VIRTIUM_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define VIRTIUM_NVME
+
+#include "cmd.h"
+#include "plugin.h"
+
+PLUGIN(NAME("virtium", "Virtium vendor specific extensions", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("save-smart-to-vtview-log", "Periodically save smart attributes into a log file.\n\
+ The data in this log file can be analyzed using excel or using Virtium’s vtView.\n\
+ Visit vtView.virtium.com to see full potential uses of the data", vt_save_smart_to_vtview_log)
+ ENTRY("show-identify", "Shows detail features and current settings", vt_show_identify)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c
new file mode 100644
index 0000000..cf185be
--- /dev/null
+++ b/plugins/wdc/wdc-nvme.c
@@ -0,0 +1,11100 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2015-2018 Western Digital Corporation or its affiliates.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * 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>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "util/types.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "wdc-nvme.h"
+#include "wdc-utils.h"
+
+#define WRITE_SIZE (sizeof(__u8) * 4096)
+
+#define WDC_NVME_SUBCMD_SHIFT 8
+
+#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
+#define WDC_NVME_SNDK_VID 0x15b7
+
+#define WDC_NVME_SN100_DEV_ID 0x0003
+#define WDC_NVME_SN200_DEV_ID 0x0023
+#define WDC_NVME_SN630_DEV_ID 0x2200
+#define WDC_NVME_SN630_DEV_ID_1 0x2201
+#define WDC_NVME_SN840_DEV_ID 0x2300
+#define WDC_NVME_SN840_DEV_ID_1 0x2500
+#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_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_SN650_DEV_ID_4 0x2721
+#define WDC_NVME_SN655_DEV_ID 0x2722
+#define WDC_NVME_SN860_DEV_ID 0x2730
+#define WDC_NVME_SN660_DEV_ID 0x2704
+
+/* This id's are no longer supported, delete ?? */
+#define WDC_NVME_SN550_DEV_ID 0x2708
+#define WDC_NVME_SN560_DEV_ID_1 0x2712
+#define WDC_NVME_SN560_DEV_ID_2 0x2713
+#define WDC_NVME_SN560_DEV_ID_3 0x2714
+
+#define WDC_NVME_SXSLCL_DEV_ID 0x2001
+#define WDC_NVME_SN520_DEV_ID 0x5003
+#define WDC_NVME_SN520_DEV_ID_1 0x5004
+#define WDC_NVME_SN520_DEV_ID_2 0x5005
+#define WDC_NVME_SN530_DEV_ID 0x5009
+#define WDC_NVME_SN720_DEV_ID 0x5002
+#define WDC_NVME_SN730A_DEV_ID 0x5006
+#define WDC_NVME_SN740_DEV_ID 0x5015
+#define WDC_NVME_SN740_DEV_ID_1 0x5016
+#define WDC_NVME_SN740_DEV_ID_2 0x5017
+#define WDC_NVME_SN740_DEV_ID_3 0x5025
+#define WDC_NVME_SN340_DEV_ID 0x500d
+#define WDC_NVME_ZN350_DEV_ID 0x5010
+#define WDC_NVME_ZN350_DEV_ID_1 0x5018
+#define WDC_NVME_SN810_DEV_ID 0x5011
+#define WDC_NVME_SN820CL_DEV_ID 0x5037
+
+#define WDC_DRIVE_CAP_CAP_DIAG 0x0000000000000001
+#define WDC_DRIVE_CAP_INTERNAL_LOG 0x0000000000000002
+#define WDC_DRIVE_CAP_C1_LOG_PAGE 0x0000000000000004
+#define WDC_DRIVE_CAP_CA_LOG_PAGE 0x0000000000000008
+#define WDC_DRIVE_CAP_D0_LOG_PAGE 0x0000000000000010
+#define WDC_DRIVE_CAP_DRIVE_STATUS 0x0000000000000020
+#define WDC_DRIVE_CAP_CLEAR_ASSERT 0x0000000000000040
+#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_FW_ACTIVATE_HISTORY 0x0000000000002000
+#define WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY 0x0000000000004000
+#define WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG 0x0000000000008000
+#define WDC_DRIVE_CAP_REASON_ID 0x0000000000010000
+#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_HW_REV_LOG_PAGE 0x0000000010000000
+#define WDC_DRIVE_CAP_C3_LOG_PAGE 0x0000000020000000
+#define WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION 0x0000000040000000
+#define WDC_DRIVE_CAP_CLOUD_LOG_PAGE 0x0000000080000000
+
+#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_DUI 0x0000000800000000
+#define WDC_DRIVE_CAP_PURGE 0x0000001000000000
+#define WDC_DRIVE_CAP_OCP_C1_LOG_PAGE 0x0000002000000000
+#define WDC_DRIVE_CAP_OCP_C4_LOG_PAGE 0x0000004000000000
+#define WDC_DRIVE_CAP_OCP_C5_LOG_PAGE 0x0000008000000000
+#define WDC_DRIVE_CAP_DEVICE_WAF 0x0000010000000000
+#define WDC_DRIVE_CAP_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
+#define SN730_GET_KEY_LOG_LENGTH 0x00090009
+#define SN730_GET_COREDUMP_LOG_LENGTH 0x00120009
+#define SN730_GET_EXTENDED_LOG_LENGTH 0x00420009
+
+#define SN730_GET_FULL_LOG_SUBOPCODE 0x00010009
+#define SN730_GET_KEY_LOG_SUBOPCODE 0x00020009
+#define SN730_GET_CORE_LOG_SUBOPCODE 0x00030009
+#define SN730_GET_EXTEND_LOG_SUBOPCODE 0x00040009
+#define SN730_LOG_CHUNK_SIZE 0x1000
+
+/* Customer ID's */
+#define WDC_CUSTOMER_ID_GN 0x0001
+#define WDC_CUSTOMER_ID_GD 0x0101
+#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_INVALID_CUSTOMER_ID -1
+
+#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
+#define WDC_NVME_DRIVE_RESIZE_SUBCMD 0x01
+
+/* Namespace Resize */
+#define WDC_NVME_NAMESPACE_RESIZE_OPCODE 0xFB
+
+/* Drive Info */
+#define WDC_NVME_DRIVE_INFO_OPCODE 0xC6
+#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
+#define WDC_NVME_CAP_DIAG_CMD_OPCODE 0xC6
+#define WDC_NVME_CAP_DIAG_SUBCMD 0x00
+#define WDC_NVME_CAP_DIAG_CMD 0x00
+
+#define WDC_NVME_CRASH_DUMP_TYPE 1
+#define WDC_NVME_PFAIL_DUMP_TYPE 2
+
+/* Capture Device Unit Info */
+#define WDC_NVME_CAP_DUI_HEADER_SIZE 0x400
+#define WDC_NVME_CAP_DUI_OPCODE 0xFA
+#define WDC_NVME_CAP_DUI_DISABLE_IO 0x01
+#define WDC_NVME_DUI_MAX_SECTION 0x3A
+#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
+#define WDC_TELEMETRY_TYPE_HOST 0x1
+#define WDC_TELEMETRY_TYPE_CONTROLLER 0x2
+#define WDC_TELEMETRY_HEADER_LENGTH 512
+#define WDC_TELEMETRY_BLOCK_SIZE 512
+
+/* Crash dump */
+#define WDC_NVME_CRASH_DUMP_SIZE_DATA_LEN WDC_NVME_LOG_SIZE_DATA_LEN
+#define WDC_NVME_CRASH_DUMP_SIZE_NDT 0x02
+#define WDC_NVME_CRASH_DUMP_SIZE_CMD 0x20
+#define WDC_NVME_CRASH_DUMP_SIZE_SUBCMD 0x03
+
+#define WDC_NVME_CRASH_DUMP_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE
+#define WDC_NVME_CRASH_DUMP_CMD 0x20
+#define WDC_NVME_CRASH_DUMP_SUBCMD 0x04
+
+/* PFail Crash dump */
+#define WDC_NVME_PF_CRASH_DUMP_SIZE_DATA_LEN WDC_NVME_LOG_SIZE_HDR_LEN
+#define WDC_NVME_PF_CRASH_DUMP_SIZE_NDT 0x02
+#define WDC_NVME_PF_CRASH_DUMP_SIZE_CMD 0x20
+#define WDC_NVME_PF_CRASH_DUMP_SIZE_SUBCMD 0x05
+
+#define WDC_NVME_PF_CRASH_DUMP_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE
+#define WDC_NVME_PF_CRASH_DUMP_CMD 0x20
+#define WDC_NVME_PF_CRASH_DUMP_SUBCMD 0x06
+
+/* Drive Log */
+#define WDC_NVME_DRIVE_LOG_SIZE_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE
+#define WDC_NVME_DRIVE_LOG_SIZE_DATA_LEN WDC_NVME_LOG_SIZE_DATA_LEN
+#define WDC_NVME_DRIVE_LOG_SIZE_NDT 0x02
+#define WDC_NVME_DRIVE_LOG_SIZE_CMD 0x20
+#define WDC_NVME_DRIVE_LOG_SIZE_SUBCMD 0x01
+
+#define WDC_NVME_DRIVE_LOG_OPCODE WDC_NVME_CAP_DIAG_CMD_OPCODE
+#define WDC_NVME_DRIVE_LOG_CMD 0x20
+#define WDC_NVME_DRIVE_LOG_SUBCMD 0x00
+
+/* Purge and Purge Monitor */
+#define WDC_NVME_PURGE_CMD_OPCODE 0xDD
+#define WDC_NVME_PURGE_MONITOR_OPCODE 0xDE
+#define WDC_NVME_PURGE_MONITOR_DATA_LEN 0x2F
+#define WDC_NVME_PURGE_MONITOR_CMD_CDW10 0x0000000C
+#define WDC_NVME_PURGE_MONITOR_TIMEOUT 0x7530
+#define WDC_NVME_PURGE_CMD_SEQ_ERR 0x0C
+#define WDC_NVME_PURGE_INT_DEV_ERR 0x06
+
+#define WDC_NVME_PURGE_STATE_IDLE 0x00
+#define WDC_NVME_PURGE_STATE_DONE 0x01
+#define WDC_NVME_PURGE_STATE_BUSY 0x02
+#define WDC_NVME_PURGE_STATE_REQ_PWR_CYC 0x03
+#define WDC_NVME_PURGE_STATE_PWR_CYC_PURGE 0x04
+
+/* Clear dumps */
+#define WDC_NVME_CLEAR_DUMP_OPCODE 0xFF
+#define WDC_NVME_CLEAR_CRASH_DUMP_CMD 0x03
+#define WDC_NVME_CLEAR_CRASH_DUMP_SUBCMD 0x05
+#define WDC_NVME_CLEAR_PF_CRASH_DUMP_SUBCMD 0x06
+
+/* Clear FW Activate History */
+#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
+#define WDC_NVME_ADD_LOG_OPCODE 0xC1
+#define WDC_GET_LOG_PAGE_SSD_PERFORMANCE 0x37
+#define WDC_NVME_GET_STAT_PERF_INTERVAL_LIFETIME 0x0F
+
+/* C2 Log Page */
+#define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE 0xC2
+#define WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 0xC8
+#define WDC_C2_LOG_BUF_LEN 0x1000
+#define WDC_C2_LOG_PAGES_SUPPORTED_ID 0x08
+#define WDC_C2_CUSTOMER_ID_ID 0x15
+#define WDC_C2_THERMAL_THROTTLE_STATUS_ID 0x18
+#define WDC_C2_ASSERT_DUMP_PRESENT_ID 0x19
+#define WDC_C2_USER_EOL_STATUS_ID 0x1A
+#define WDC_C2_USER_EOL_STATE_ID 0x1C
+#define WDC_C2_SYSTEM_EOL_STATE_ID 0x1D
+#define WDC_C2_FORMAT_CORRUPT_REASON_ID 0x1E
+#define WDC_EOL_STATUS_NORMAL cpu_to_le32(0x00000000)
+#define WDC_EOL_STATUS_END_OF_LIFE cpu_to_le32(0x00000001)
+#define WDC_EOL_STATUS_READ_ONLY cpu_to_le32(0x00000002)
+#define WDC_ASSERT_DUMP_NOT_PRESENT cpu_to_le32(0x00000000)
+#define WDC_ASSERT_DUMP_PRESENT cpu_to_le32(0x00000001)
+#define WDC_THERMAL_THROTTLING_OFF cpu_to_le32(0x00000000)
+#define WDC_THERMAL_THROTTLING_ON cpu_to_le32(0x00000001)
+#define WDC_THERMAL_THROTTLING_UNAVAILABLE cpu_to_le32(0x00000002)
+#define WDC_FORMAT_NOT_CORRUPT cpu_to_le32(0x00000000)
+#define WDC_FORMAT_CORRUPT_FW_ASSERT cpu_to_le32(0x00000001)
+#define WDC_FORMAT_CORRUPT_UNKNOWN cpu_to_le32(0x000000FF)
+
+/* CA Log Page */
+#define WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE 0xCA
+#define WDC_FB_CA_LOG_BUF_LEN 0x80
+#define WDC_BD_CA_LOG_BUF_LEN 0xA0 /* Added 4 padding bytes to resolve build warning messages */
+
+/* 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_ID 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
+#define WDC_C2_GUID_LENGTH 16
+
+/* C3 Latency Monitor Log Page */
+#define WDC_LATENCY_MON_LOG_BUF_LEN 0x200
+#define WDC_LATENCY_MON_LOG_ID 0xC3
+#define WDC_LATENCY_MON_VERSION 0x0001
+
+#define WDC_C3_GUID_LENGTH 16
+static __u8 wdc_lat_mon_guid[WDC_C3_GUID_LENGTH] = { 0x92, 0x7a, 0xc0, 0x8c, 0xd0, 0x84, 0x6c, 0x9c,
+ 0x70, 0x43, 0xe6, 0xd4, 0x58, 0x5e, 0xd4, 0x85 };
+
+/* D0 Smart Log Page */
+#define WDC_NVME_GET_VU_SMART_LOG_OPCODE 0xD0
+#define WDC_NVME_VU_SMART_LOG_LEN 0x200
+
+/* 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_C3 0xC3
+#define WDC_LOG_ID_C4 0xC4
+#define WDC_LOG_ID_C5 0xC5
+#define WDC_LOG_ID_C6 0xC6
+#define WDC_LOG_ID_C8 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8
+#define WDC_LOG_ID_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
+#define WDC_LOG_ID_DE 0xDE
+#define WDC_LOG_ID_F0 0xF0
+#define WDC_LOG_ID_F1 0xF1
+#define WDC_LOG_ID_F2 0xF2
+#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_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
+#define WDC_NVME_CLEAR_ASSERT_DUMP_SUBCMD 0x05
+
+#define WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID 0xD2
+
+/* Drive Essentials */
+#define WDC_DE_DEFAULT_NUMBER_OF_ERROR_ENTRIES 64
+#define WDC_DE_GENERIC_BUFFER_SIZE 80
+#define WDC_DE_GLOBAL_NSID 0xFFFFFFFF
+#define WDC_DE_DEFAULT_NAMESPACE_ID 0x01
+#define WDC_DE_PATH_SEPARATOR "/"
+#define WDC_DE_TAR_FILES "*.bin"
+#define WDC_DE_TAR_FILE_EXTN ".tar.gz"
+#define WDC_DE_TAR_CMD "tar -czf"
+
+/* VS NAND Stats */
+#define WDC_NVME_NAND_STATS_LOG_ID 0xFB
+#define WDC_NVME_NAND_STATS_SIZE 0x200
+
+/* 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
+#define WDC_DE_FILE_NAME_SIZE 32
+#define WDC_DE_VU_READ_BUFFER_STANDARD_OFFSET 0x8000
+#define WDC_DE_READ_MAX_TRANSFER_SIZE 0x8000
+
+#define WDC_DE_MANUFACTURING_INFO_PAGE_FILE_NAME "manufacturing_info" /* Unique log entry page name. */
+#define WDC_DE_CORE_DUMP_FILE_NAME "core_dump"
+#define WDC_DE_EVENT_LOG_FILE_NAME "event_log"
+#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,
+ FS_DEFAULT = 1,
+ FS_SAVED = 2,
+ FS_SUPPORTED_CAPBILITIES = 3
+} NVME_FEATURES_SELECT;
+
+typedef enum _NVME_FEATURE_IDENTIFIERS
+{
+ FID_ARBITRATION = 0x01,
+ FID_POWER_MANAGEMENT = 0x02,
+ FID_LBA_RANGE_TYPE = 0x03,
+ FID_TEMPERATURE_THRESHOLD = 0x04,
+ FID_ERROR_RECOVERY = 0x05,
+ FID_VOLATILE_WRITE_CACHE = 0x06,
+ FID_NUMBER_OF_QUEUES = 0x07,
+ FID_INTERRUPT_COALESCING = 0x08,
+ FID_INTERRUPT_VECTOR_CONFIGURATION = 0x09,
+ FID_WRITE_ATOMICITY = 0x0A,
+ FID_ASYNCHRONOUS_EVENT_CONFIGURATION = 0x0B,
+ FID_AUTONOMOUS_POWER_STATE_TRANSITION = 0x0C,
+/*Below FID's are NVM Command Set Specific*/
+ FID_SOFTWARE_PROGRESS_MARKER = 0x80,
+ FID_HOST_IDENTIFIER = 0x81,
+ FID_RESERVATION_NOTIFICATION_MASK = 0x82,
+ FID_RESERVATION_PERSISTENCE = 0x83
+} NVME_FEATURE_IDENTIFIERS;
+
+typedef enum
+{
+ WDC_DE_TYPE_IDENTIFY = 0x1,
+ WDC_DE_TYPE_SMARTATTRIBUTEDUMP = 0x2,
+ WDC_DE_TYPE_EVENTLOG = 0x4,
+ WDC_DE_TYPE_DUMPTRACE = 0x8,
+ WDC_DE_TYPE_DUMPSNAPSHOT = 0x10,
+ WDC_DE_TYPE_ATA_LOGS = 0x20,
+ WDC_DE_TYPE_SMART_LOGS = 0x40,
+ WDC_DE_TYPE_SCSI_LOGS = 0x80,
+ WDC_DE_TYPE_SCSI_MODE_PAGES = 0x100,
+ WDC_DE_TYPE_NVMe_FEATURES = 0x200,
+ WDC_DE_TYPE_DUMPSMARTERRORLOG3 = 0x400,
+ WDC_DE_TYPE_DUMPLOG3E = 0x800,
+ WDC_DE_TYPE_DUMPSCRAM = 0x1000,
+ WDC_DE_TYPE_PCU_LOG = 0x2000,
+ WDC_DE_TYPE_DUMP_ERROR_LOGS = 0x4000,
+ WDC_DE_TYPE_FW_SLOT_LOGS = 0x8000,
+ WDC_DE_TYPE_MEDIA_SETTINGS = 0x10000,
+ WDC_DE_TYPE_SMART_DATA = 0x20000,
+ WDC_DE_TYPE_NVME_SETTINGS = 0x40000,
+ WDC_DE_TYPE_NVME_ERROR_LOGS = 0x80000,
+ WDC_DE_TYPE_NVME_LOGS = 0x100000,
+ WDC_DE_TYPE_UART_LOGS = 0x200000,
+ WDC_DE_TYPE_DLOGS_SPI = 0x400000,
+ WDC_DE_TYPE_DLOGS_RAM = 0x800000,
+ WDC_DE_TYPE_NVME_MANF_INFO = 0x2000000,
+ WDC_DE_TYPE_NONE = 0x1000000,
+ WDC_DE_TYPE_ALL = 0xFFFFFFF,
+} WDC_DRIVE_ESSENTIAL_TYPE;
+
+#define WDC_C0_GUID_LENGTH 16
+#define WDC_SCA_V1_NAND_STATS 0x1
+#define WDC_SCA_V1_ALL 0xF
+typedef enum
+{
+ SCAO_V1_PMUWT = 0, /* Physical media units written TLC */
+ SCAO_V1_PMUWS = 16, /* Physical media units written SLC */
+ SCAO_V1_BUNBN = 32, /* Bad user nand blocks normalized */
+ SCAO_V1_BUNBR = 34, /* Bad user nand blocks raw */
+ SCAO_V1_XRC = 40, /* XOR recovery count */
+ SCAO_V1_UREC = 48, /* Uncorrectable read error count */
+ SCAO_V1_EECE = 56, /* End to end corrected errors */
+ SCAO_V1_EEDE = 64, /* End to end detected errors */
+ SCAO_V1_EEUE = 72, /* End to end uncorrected errors */
+ SCAO_V1_SDPU = 80, /* System data percent used */
+ SCAO_V1_MNUDEC = 84, /* Min User data erase counts (TLC) */
+ SCAO_V1_MXUDEC = 92, /* Max User data erase counts (TLC) */
+ SCAO_V1_AVUDEC = 100, /* Average User data erase counts (TLC) */
+ SCAO_V1_MNEC = 108, /* Min Erase counts (SLC) */
+ SCAO_V1_MXEC = 116, /* Max Erase counts (SLC) */
+ SCAO_V1_AVEC = 124, /* Average Erase counts (SLC) */
+ SCAO_V1_PFCN = 132, /* Program fail count normalized */
+ SCAO_V1_PFCR = 134, /* Program fail count raw */
+ SCAO_V1_EFCN = 140, /* Erase fail count normalized */
+ SCAO_V1_EFCR = 142, /* Erase fail count raw */
+ SCAO_V1_PCEC = 148, /* PCIe correctable error count */
+ SCAO_V1_PFBU = 156, /* Percent free blocks (User) */
+ SCAO_V1_SVN = 160, /* Security Version Number */
+ SCAO_V1_PFBS = 168, /* Percent free blocks (System) */
+ SCAO_V1_DCC = 172, /* Deallocate Commands Completed */
+ SCAO_V1_TNU = 188, /* Total Namespace Utilization */
+ SCAO_V1_FCC = 196, /* Format NVM Commands Completed */
+ SCAO_V1_BBPG = 198, /* Background Back-Pressure Gauge */
+ SCAO_V1_SEEC = 202, /* Soft ECC error count */
+ SCAO_V1_RFSC = 210, /* Refresh count */
+ SCAO_V1_BSNBN = 218, /* Bad system nand blocks normalized */
+ SCAO_V1_BSNBR = 220, /* Bad system nand blocks raw */
+ SCAO_V1_EEST = 226, /* Endurance estimate */
+ SCAO_V1_TTC = 242, /* Thermal throttling count */
+ SCAO_V1_UIO = 244, /* Unaligned I/O */
+ SCAO_V1_PMUR = 252, /* Physical media units read */
+ SCAO_V1_RTOC = 268, /* Read command timeout count */
+ SCAO_V1_WTOC = 272, /* Write command timeout count */
+ SCAO_V1_TTOC = 276, /* Trim command timeout count */
+ SCAO_V1_PLRC = 284, /* PCIe Link Retraining Count */
+ SCAO_V1_PSCC = 292, /* Power State Change Count */
+ SCAO_V1_MAVF = 300, /* Boot SSD major version field */
+ SCAO_V1_MIVF = 302, /* Boot SSD minor version field */
+ SCAO_V1_PVF = 304, /* Boot SSD point version field */
+ SCAO_V1_EVF = 306, /* Boot SSD errata version field */
+ SCAO_V1_FTLUS = 308, /* FTL Unit Size */
+ SCAO_V1_TCGOS = 312, /* TCG Ownership Status */
+
+ SCAO_V1_LPV = 494, /* Log page version - 0x0001 */
+ SCAO_V1_LPG = 496, /* Log page GUID */
+} SMART_CLOUD_ATTRIBUTE_OFFSETS_V1;
+
+static __u8 ext_smart_guid[WDC_C0_GUID_LENGTH] = { 0x65, 0x43, 0x88, 0x78, 0xAC, 0xD8, 0x78, 0xA1,
+ 0x66, 0x42, 0x1E, 0x0F, 0x92, 0xD7, 0x6D, 0xC4 };
+
+typedef struct __attribute__((__packed__)) wdc_nvme_ext_smart_log
+{
+ __u8 ext_smart_pmuwt[16]; /* 000 Physical media units written TLC */
+ __u8 ext_smart_pmuws[16]; /* 016 Physical media units written SLC */
+ __u8 ext_smart_bunbc[8]; /* 032 Bad user nand block count */
+ __u64 ext_smart_xrc; /* 040 XOR recovery count */
+ __u64 ext_smart_urec; /* 048 Uncorrectable read error count */
+ __u64 ext_smart_eece; /* 056 End to end corrected errors */
+ __u64 ext_smart_eede; /* 064 End to end detected errors */
+ __u64 ext_smart_eeue; /* 072 End to end uncorrected errors */
+ __u8 ext_smart_sdpu; /* 080 System data percent used */
+ __u8 ext_smart_rsvd1[3]; /* 081 reserved */
+ __u64 ext_smart_mnudec; /* 084 Min User data erase counts (TLC) */
+ __u64 ext_smart_mxudec; /* 092 Max User data erase counts (TLC) */
+ __u64 ext_smart_avudec; /* 100 Average User data erase counts (TLC) */
+ __u64 ext_smart_mnec; /* 108 Min Erase counts (SLC) */
+ __u64 ext_smart_mxec; /* 116 Max Erase counts (SLC) */
+ __u64 ext_smart_avec; /* 124 Average Erase counts (SLC) */
+ __u8 ext_smart_pfc[8]; /* 132 Program fail count */
+ __u8 ext_smart_efc[8]; /* 140 Erase fail count */
+ __u64 ext_smart_pcec; /* 148 PCIe correctable error count */
+ __u8 ext_smart_pfbu; /* 156 Percent free blocks (User) */
+ __u8 ext_smart_rsvd2[3]; /* 157 reserved */
+ __u64 ext_smart_svn; /* 160 Security Version Number */
+ __u8 ext_smart_pfbs; /* 168 Percent free blocks (System) */
+ __u8 ext_smart_rsvd3[3]; /* 169 reserved */
+ __u8 ext_smart_dcc[16]; /* 172 Deallocate Commands Completed */
+ __u64 ext_smart_tnu; /* 188 Total Namespace Utilization */
+ __u16 ext_smart_fcc; /* 196 Format NVM Commands Completed */
+ __u8 ext_smart_bbpg; /* 198 Background Back-Pressure Gauge */
+ __u8 ext_smart_rsvd4[3]; /* 199 reserved */
+ __u64 ext_smart_seec; /* 202 Soft ECC error count */
+ __u64 ext_smart_rfsc; /* 210 Refresh count */
+ __u8 ext_smart_bsnbc[8]; /* 218 Bad system nand block count */
+ __u8 ext_smart_eest[16]; /* 226 Endurance estimate */
+ __u16 ext_smart_ttc; /* 242 Thermal throttling count */
+ __u64 ext_smart_uio; /* 244 Unaligned I/O */
+ __u8 ext_smart_pmur[16]; /* 252 Physical media units read */
+ __u32 ext_smart_rtoc; /* 268 Read command timeout count */
+ __u32 ext_smart_wtoc; /* 272 Write command timeout count */
+ __u32 ext_smart_ttoc; /* 276 Trim command timeout count */
+ __u8 ext_smart_rsvd5[4]; /* 280 reserved */
+ __u64 ext_smart_plrc; /* 284 PCIe Link Retraining Count */
+ __u64 ext_smart_pscc; /* 292 Power State Change Count */
+ __u16 ext_smart_maj; /* 300 Boot SSD major version field */
+ __u16 ext_smart_min; /* 302 Boot SSD minor version field */
+ __u16 ext_smart_pt; /* 304 Boot SSD point version field */
+ __u16 ext_smart_err; /* 306 Boot SSD errata version field */
+ __u32 ext_smart_ftlus; /* 308 FTL Unit Size */
+ __u32 ext_smart_tcgos; /* 312 TCG Ownership Status */
+ __u8 ext_smart_rsvd6[178]; /* 316 reserved */
+ __u16 ext_smart_lpv; /* 494 Log page version - 0x0001 */
+ __u8 ext_smart_lpg[16]; /* 496 Log page GUID */
+} wdc_nvme_ext_smart_log;
+
+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_PSCC = 200, /* Power State Change Count */
+ SCAO_LPV = 494, /* Log page version */
+ SCAO_LPG = 496, /* Log page GUID */
+} SMART_CLOUD_ATTRIBUTE_OFFSETS_V3;
+
+static __u8 scao_guid[WDC_C0_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;
+
+#define WDC_NVME_C6_GUID_LENGTH 16
+#define WDC_NVME_GET_HW_REV_LOG_OPCODE 0xc6
+#define WDC_NVME_HW_REV_LOG_PAGE_LEN 512
+
+typedef struct __attribute__((__packed__)) wdc_nvme_hw_rev_log
+{
+ __u8 hw_rev_gdr; /* 0 Global Device HW Revision */
+ __u8 hw_rev_ar; /* 1 ASIC HW Revision */
+ __u8 hw_rev_pbc_mc; /* 2 PCB Manufacturer Code */
+ __u8 hw_rev_dram_mc; /* 3 DRAM Manufacturer Code */
+ __u8 hw_rev_nand_mc; /* 4 NAND Manufacturer Code */
+ __u8 hw_rev_pmic1_mc; /* 5 PMIC 1 Manufacturer Code */
+ __u8 hw_rev_pmic2_mc; /* 6 PMIC 2 Manufacturer Code */
+ __u8 hw_rev_c1_mc; /* 7 Other Component 1 Manf Code */
+ __u8 hw_rev_c2_mc; /* 8 Other Component 2 Manf Code */
+ __u8 hw_rev_c3_mc; /* 9 Other Component 3 Manf Code */
+ __u8 hw_rev_c4_mc; /* 10 Other Component 4 Manf Code */
+ __u8 hw_rev_c5_mc; /* 11 Other Component 5 Manf Code */
+ __u8 hw_rev_c6_mc; /* 12 Other Component 6 Manf Code */
+ __u8 hw_rev_c7_mc; /* 13 Other Component 7 Manf Code */
+ __u8 hw_rev_c8_mc; /* 14 Other Component 8 Manf Code */
+ __u8 hw_rev_c9_mc; /* 15 Other Component 9 Manf Code */
+ __u8 hw_rev_rsrvd1[48]; /* 16 Reserved 48 bytes */
+ __u8 hw_rev_dev_mdi[16]; /* 64 Device Manf Detailed Info */
+ __u8 hw_rev_asic_di[16]; /* 80 ASIC Detailed Info */
+ __u8 hw_rev_pcb_di[16]; /* 96 PCB Detailed Info */
+ __u8 hw_rev_dram_di[16]; /* 112 DRAM Detailed Info */
+ __u8 hw_rev_nand_di[16]; /* 128 NAND Detailed Info */
+ __u8 hw_rev_pmic1_di[16]; /* 144 PMIC1 Detailed Info */
+ __u8 hw_rev_pmic2_di[16]; /* 160 PMIC2 Detailed Info */
+ __u8 hw_rev_c1_di[16]; /* 176 Component 1 Detailed Info */
+ __u8 hw_rev_c2_di[16]; /* 192 Component 2 Detailed Info */
+ __u8 hw_rev_c3_di[16]; /* 208 Component 3 Detailed Info */
+ __u8 hw_rev_c4_di[16]; /* 224 Component 4 Detailed Info */
+ __u8 hw_rev_c5_di[16]; /* 240 Component 5 Detailed Info */
+ __u8 hw_rev_c6_di[16]; /* 256 Component 6 Detailed Info */
+ __u8 hw_rev_c7_di[16]; /* 272 Component 7 Detailed Info */
+ __u8 hw_rev_c8_di[16]; /* 288 Component 8 Detailed Info */
+ __u8 hw_rev_c9_di[16]; /* 304 Component 9 Detailed Info */
+ __u8 hw_rev_sn[32]; /* 320 Serial Number */
+ __u8 hw_rev_rsrvd2[142]; /* 352 Reserved 143 bytes */
+ __u16 hw_rev_version; /* 494 Log Page Version */
+ __u8 hw_rev_guid[16]; /* 496 Log Page GUID */
+} wdc_nvme_hw_rev_log;
+
+static __u8 hw_rev_log_guid[WDC_NVME_C6_GUID_LENGTH] = { 0xAA, 0xB0, 0x05, 0xF5, 0x13, 0x5E, 0x48, 0x15,
+ 0xAB, 0x89, 0x05, 0xBA, 0x8B, 0xE2, 0xBF, 0x3C };
+
+typedef struct __attribute__((__packed__)) _WDC_DE_VU_FILE_META_DATA
+{
+ __u8 fileName[WDC_DE_FILE_NAME_SIZE];
+ __u16 fileID;
+ __u64 fileSize;
+} WDC_DE_VU_FILE_META_DATA, *PWDC_DE_VU_FILE_META_DATA;
+
+typedef struct _WDC_DRIVE_ESSENTIALS
+{
+ WDC_DE_VU_FILE_META_DATA metaData;
+ WDC_DRIVE_ESSENTIAL_TYPE essentialType;
+} WDC_DRIVE_ESSENTIALS;
+
+typedef struct _WDC_DE_VU_LOG_DIRECTORY
+{
+ WDC_DRIVE_ESSENTIALS *logEntry; /* Caller to allocate memory */
+ __u32 maxNumLogEntries; /* Caller to input memory allocated */
+ __u32 numOfValidLogEntries; /* API will output this value */
+} WDC_DE_VU_LOG_DIRECTORY,*PWDC_DE_VU_LOG_DIRECTORY;
+
+typedef struct _WDC_DE_CSA_FEATURE_ID_LIST
+{
+ NVME_FEATURE_IDENTIFIERS featureId;
+ __u8 featureName[WDC_DE_GENERIC_BUFFER_SIZE];
+} WDC_DE_CSA_FEATURE_ID_LIST;
+
+typedef struct tarfile_metadata {
+ char fileName[MAX_PATH_LEN];
+ int8_t bufferFolderPath[MAX_PATH_LEN];
+ char bufferFolderName[MAX_PATH_LEN];
+ char tarFileName[MAX_PATH_LEN];
+ char tarFiles[MAX_PATH_LEN];
+ char tarCmd[MAX_PATH_LEN+MAX_PATH_LEN];
+ char currDir[MAX_PATH_LEN];
+ UtilsTimeInfo timeInfo;
+ uint8_t* timeString[MAX_PATH_LEN];
+} tarfile_metadata;
+
+static WDC_DE_CSA_FEATURE_ID_LIST deFeatureIdList[] =
+{
+ {0x00 , "Dummy Placeholder"},
+ {FID_ARBITRATION , "Arbitration"},
+ {FID_POWER_MANAGEMENT , "PowerMgmnt"},
+ {FID_LBA_RANGE_TYPE , "LbaRangeType"},
+ {FID_TEMPERATURE_THRESHOLD , "TempThreshold"},
+ {FID_ERROR_RECOVERY , "ErrorRecovery"},
+ {FID_VOLATILE_WRITE_CACHE , "VolatileWriteCache"},
+ {FID_NUMBER_OF_QUEUES , "NumOfQueues"},
+ {FID_INTERRUPT_COALESCING , "InterruptCoalesing"},
+ {FID_INTERRUPT_VECTOR_CONFIGURATION , "InterruptVectorConfig"},
+ {FID_WRITE_ATOMICITY , "WriteAtomicity"},
+ {FID_ASYNCHRONOUS_EVENT_CONFIGURATION , "AsynEventConfig"},
+ {FID_AUTONOMOUS_POWER_STATE_TRANSITION , "AutonomousPowerState"},
+};
+
+typedef enum _NVME_VU_DE_LOGPAGE_NAMES
+{
+ NVME_DE_LOGPAGE_E3 = 0x01,
+ NVME_DE_LOGPAGE_C0 = 0x02
+} NVME_VU_DE_LOGPAGE_NAMES;
+typedef struct _NVME_VU_DE_LOGPAGE_LIST
+{
+ NVME_VU_DE_LOGPAGE_NAMES logPageName;
+ __u32 logPageId;
+ __u32 logPageLen;
+ char logPageIdStr[5];
+} NVME_VU_DE_LOGPAGE_LIST, *PNVME_VU_DE_LOGPAGE_LIST;
+
+typedef struct _WDC_NVME_DE_VU_LOGPAGES
+{
+ NVME_VU_DE_LOGPAGE_NAMES vuLogPageReqd;
+ __u32 numOfVULogPages;
+} WDC_NVME_DE_VU_LOGPAGES, *PWDC_NVME_DE_VU_LOGPAGES;
+
+static NVME_VU_DE_LOGPAGE_LIST deVULogPagesList[] =
+{
+ { NVME_DE_LOGPAGE_E3, 0xE3, 1072, "0xe3"},
+ { NVME_DE_LOGPAGE_C0, 0xC0, 512, "0xc0"}
+};
+
+static int wdc_get_serial_name(struct nvme_dev *dev, char *file, size_t len,
+ const char *suffix);
+static int wdc_create_log_file(char *file, __u8 *drive_log_data,
+ __u32 drive_log_length);
+static int wdc_do_clear_dump(struct nvme_dev *dev, __u8 opcode, __u32 cdw12);
+static int wdc_do_dump(struct nvme_dev *dev, __u32 opcode,__u32 data_len,
+ __u32 cdw12, char *file, __u32 xfer_size);
+static int wdc_do_crash_dump(struct nvme_dev *dev, char *file, int type);
+static int wdc_crash_dump(struct nvme_dev *dev, char *file, int type);
+static int wdc_get_crash_dump(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static int wdc_do_drive_log(struct nvme_dev *dev, char *file);
+static int wdc_drive_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static const char* wdc_purge_mon_status_to_string(__u32 status);
+static int wdc_purge(int argc, char **argv,
+ struct command *command, struct plugin *plugin);
+static int wdc_purge_monitor(int argc, char **argv,
+ struct command *command, struct plugin *plugin);
+static bool wdc_nvme_check_supported_log_page(nvme_root_t r,
+ struct nvme_dev *dev, __u8 log_id);
+static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static int wdc_do_drive_essentials(nvme_root_t r, struct nvme_dev *dev, char *dir, char *key);
+static int wdc_drive_essentials(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static int wdc_drive_status(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static int wdc_clear_assert_dump(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static int wdc_drive_resize(int argc, char **argv,
+ struct command *command, struct plugin *plugin);
+static int wdc_do_drive_resize(struct nvme_dev *dev, uint64_t new_size);
+static int wdc_namespace_resize(int argc, char **argv,
+ struct command *command, struct plugin *plugin);
+static int wdc_do_namespace_resize(struct nvme_dev *dev, __u32 nsid,
+ __u32 op_option);
+static int wdc_reason_identifier(int argc, char **argv,
+ struct command *command, struct plugin *plugin);
+static int wdc_do_get_reason_id(struct nvme_dev *dev, char *file, int log_id);
+static int wdc_save_reason_id(struct nvme_dev *dev, __u8 *rsn_ident, int size);
+static int wdc_clear_reason_id(struct nvme_dev *dev);
+static int wdc_log_page_directory(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static int wdc_do_drive_info(struct nvme_dev *dev, __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(nvme_root_t r, struct nvme_dev *dev);
+static int wdc_enc_get_nic_log(struct nvme_dev *dev, __u8 log_id,
+ __u32 xfer_size, __u32 data_len, FILE *out);
+static int wdc_enc_submit_move_data(struct nvme_dev *dev, char *cmd, int len,
+ int xfer_size, FILE *out, int data_id, int cdw14, int cdw15);
+static bool get_dev_mgment_cbs_data(nvme_root_t r, struct nvme_dev *dev,
+ __u8 log_id, void **cbs_data);
+static __u32 wdc_get_fw_cust_id(nvme_root_t r, struct nvme_dev *dev);
+
+/* Drive log data size */
+struct wdc_log_size {
+ __le32 log_size;
+};
+
+/* E6 log header */
+struct wdc_e6_log_hdr {
+ __le32 eye_catcher;
+ __u8 log_size[4];
+};
+
+/* DUI log header */
+struct wdc_dui_log_section {
+ __le16 section_type;
+ __le16 data_area_id;
+ __le32 section_size;
+};
+
+/* DUI log header V2 */
+struct __attribute__((__packed__)) wdc_dui_log_section_v2 {
+ __le16 section_type;
+ __le16 data_area_id;
+ __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;
+ __le16 section_count;
+ __le32 log_size;
+ struct wdc_dui_log_section log_section[WDC_NVME_DUI_MAX_SECTION];
+ __u8 log_data[40];
+};
+
+struct __attribute__((__packed__)) wdc_dui_log_hdr_v2 {
+ __u8 telemetry_hdr[512];
+ __u8 hdr_version;
+ __u8 product_id;
+ __le16 section_count;
+ __le64 log_size;
+ struct wdc_dui_log_section_v2 log_section[WDC_NVME_DUI_MAX_SECTION_V2];
+ __u8 log_data[40];
+};
+
+struct __attribute__((__packed__)) wdc_dui_log_hdr_v3 {
+ __u8 telemetry_hdr[512];
+ __u8 hdr_version;
+ __u8 product_id;
+ __le16 section_count;
+ __le64 log_size;
+ struct wdc_dui_log_section_v2 log_section[WDC_NVME_DUI_MAX_SECTION_V3];
+ __u8 securityNonce[36];
+ __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;
+ __le16 rsvd2;
+ __le16 first_erase_failure_cnt;
+ __le16 second_erase_failure_cnt;
+ __le16 rsvd3;
+ __le16 programm_failure_cnt;
+ __le32 rsvd4;
+ __le32 rsvd5;
+ __le32 entire_progress_total;
+ __le32 entire_progress_current;
+ __u8 rsvd6[14];
+};
+
+/* Additional Smart Log */
+struct wdc_log_page_header {
+ uint8_t num_subpages;
+ uint8_t reserved;
+ __le16 total_log_size;
+};
+
+struct wdc_log_page_subpage_header {
+ uint8_t spcode;
+ uint8_t pcset;
+ __le16 subpage_length;
+};
+
+struct wdc_ssd_perf_stats {
+ __le64 hr_cmds; /* Host Read Commands */
+ __le64 hr_blks; /* Host Read Blocks */
+ __le64 hr_ch_cmds; /* Host Read Cache Hit Commands */
+ __le64 hr_ch_blks; /* Host Read Cache Hit Blocks */
+ __le64 hr_st_cmds; /* Host Read Stalled Commands */
+ __le64 hw_cmds; /* Host Write Commands */
+ __le64 hw_blks; /* Host Write Blocks */
+ __le64 hw_os_cmds; /* Host Write Odd Start Commands */
+ __le64 hw_oe_cmds; /* Host Write Odd End Commands */
+ __le64 hw_st_cmds; /* Host Write Commands Stalled */
+ __le64 nr_cmds; /* NAND Read Commands */
+ __le64 nr_blks; /* NAND Read Blocks */
+ __le64 nw_cmds; /* NAND Write Commands */
+ __le64 nw_blks; /* NAND Write Blocks */
+ __le64 nrbw; /* NAND Read Before Write */
+};
+
+/* Additional C2 Log Page */
+struct wdc_c2_log_page_header {
+ __le32 length;
+ __le32 version;
+};
+
+struct wdc_c2_log_subpage_header {
+ __le32 length;
+ __le32 entry_id;
+ __le32 data;
+};
+
+struct wdc_c2_cbs_data {
+ __le32 length;
+ __u8 data[];
+};
+
+struct __attribute__((__packed__)) wdc_bd_ca_log_format {
+ __u8 field_id;
+ __u8 reserved1[2];
+ __u8 normalized_value;
+ __u8 raw_value[8];
+};
+
+#define READ 0
+#define WRITE 1
+#define TRIM 2
+#define RESERVED 3
+
+struct __attribute__((__packed__)) wdc_ssd_latency_monitor_log {
+ __u8 feature_status; /* 0x00 */
+ __u8 rsvd1; /* 0x01 */
+ __le16 active_bucket_timer; /* 0x02 */
+ __le16 active_bucket_timer_threshold; /* 0x04 */
+ __u8 active_threshold_a; /* 0x06 */
+ __u8 active_threshold_b; /* 0x07 */
+ __u8 active_threshold_c; /* 0x08 */
+ __u8 active_threshold_d; /* 0x09 */
+ __le16 active_latency_config; /* 0x0A */
+ __u8 active_latency_min_window; /* 0x0C */
+ __u8 rsvd2[0x13]; /* 0x0D */
+
+ __le32 active_bucket_counter[4][4] ; /* 0x20 - 0x5F */
+ __le64 active_latency_timestamp[4][3]; /* 0x60 - 0xBF */
+ __le16 active_measured_latency[4][3]; /* 0xC0 - 0xD7 */
+ __le16 active_latency_stamp_units; /* 0xD8 */
+ __u8 rsvd3[0x16]; /* 0xDA */
+
+ __le32 static_bucket_counter[4][4] ; /* 0xF0 - 0x12F */
+ __le64 static_latency_timestamp[4][3]; /* 0x130 - 0x18F */
+ __le16 static_measured_latency[4][3]; /* 0x190 - 0x1A7 */
+ __le16 static_latency_stamp_units; /* 0x1A8 */
+ __u8 rsvd4[0x16]; /* 0x1AA */
+
+ __le16 debug_log_trigger_enable; /* 0x1C0 */
+ __le16 debug_log_measured_latency; /* 0x1C2 */
+ __le64 debug_log_latency_stamp; /* 0x1C4 */
+ __le16 debug_log_ptr; /* 0x1CC */
+ __le16 debug_log_counter_trigger; /* 0x1CE */
+ __u8 debug_log_stamp_units; /* 0x1D0 */
+ __u8 rsvd5[0x1D]; /* 0x1D1 */
+
+ __le16 log_page_version; /* 0x1EE */
+ __u8 log_page_guid[0x10]; /* 0x1F0 */
+};
+
+struct __attribute__((__packed__)) wdc_ssd_ca_perf_stats {
+ __le64 nand_bytes_wr_lo; /* 0x00 - NAND Bytes Written lo */
+ __le64 nand_bytes_wr_hi; /* 0x08 - NAND Bytes Written hi */
+ __le64 nand_bytes_rd_lo; /* 0x10 - NAND Bytes Read lo */
+ __le64 nand_bytes_rd_hi; /* 0x18 - NAND Bytes Read hi */
+ __le64 nand_bad_block; /* 0x20 - NAND Bad Block Count */
+ __le64 uncorr_read_count; /* 0x28 - Uncorrectable Read Count */
+ __le64 ecc_error_count; /* 0x30 - Soft ECC Error Count */
+ __le32 ssd_detect_count; /* 0x38 - SSD End to End Detection Count */
+ __le32 ssd_correct_count; /* 0x3C - SSD End to End Correction Count */
+ __u8 data_percent_used; /* 0x40 - System Data Percent Used */
+ __le32 data_erase_max; /* 0x41 - User Data Erase Counts */
+ __le32 data_erase_min; /* 0x45 - User Data Erase Counts */
+ __le64 refresh_count; /* 0x49 - Refresh Count */
+ __le64 program_fail; /* 0x51 - Program Fail Count */
+ __le64 user_erase_fail; /* 0x59 - User Data Erase Fail Count */
+ __le64 system_erase_fail; /* 0x61 - System Area Erase Fail Count */
+ __u8 thermal_throttle_status; /* 0x69 - Thermal Throttling Status */
+ __u8 thermal_throttle_count; /* 0x6A - Thermal Throttling Count */
+ __le64 pcie_corr_error; /* 0x6B - pcie Correctable Error Count */
+ __le32 incomplete_shutdown_count; /* 0x73 - Incomplete Shutdown Count */
+ __u8 percent_free_blocks; /* 0x77 - Percent Free Blocks */
+ __u8 rsvd[392]; /* 0x78 - Reserved bytes 120-511 */
+};
+
+struct __attribute__((__packed__)) wdc_ssd_d0_smart_log {
+ __le32 smart_log_page_header; /* 0x00 - Smart Log Page Header */
+ __le32 lifetime_realloc_erase_block_count; /* 0x04 - Lifetime reallocated erase block count */
+ __le32 lifetime_power_on_hours; /* 0x08 - Lifetime power on hours */
+ __le32 lifetime_uecc_count; /* 0x0C - Lifetime UECC count */
+ __le32 lifetime_wrt_amp_factor; /* 0x10 - Lifetime write amplification factor */
+ __le32 trailing_hr_wrt_amp_factor; /* 0x14 - Trailing hour write amplification factor */
+ __le32 reserve_erase_block_count; /* 0x18 - Reserve erase block count */
+ __le32 lifetime_program_fail_count; /* 0x1C - Lifetime program fail count */
+ __le32 lifetime_block_erase_fail_count; /* 0x20 - Lifetime block erase fail count */
+ __le32 lifetime_die_failure_count; /* 0x24 - Lifetime die failure count */
+ __le32 lifetime_link_rate_downgrade_count; /* 0x28 - Lifetime link rate downgrade count */
+ __le32 lifetime_clean_shutdown_count; /* 0x2C - Lifetime clean shutdown count on power loss */
+ __le32 lifetime_unclean_shutdown_count; /* 0x30 - Lifetime unclean shutdowns on power loss */
+ __le32 current_temp; /* 0x34 - Current temperature */
+ __le32 max_recorded_temp; /* 0x38 - Max recorded temperature */
+ __le32 lifetime_retired_block_count; /* 0x3C - Lifetime retired block count */
+ __le32 lifetime_read_disturb_realloc_events; /* 0x40 - Lifetime read disturb reallocation events */
+ __le64 lifetime_nand_writes; /* 0x44 - Lifetime NAND write Lpages */
+ __le32 capacitor_health; /* 0x4C - Capacitor health */
+ __le64 lifetime_user_writes; /* 0x50 - Lifetime user writes */
+ __le64 lifetime_user_reads; /* 0x58 - Lifetime user reads */
+ __le32 lifetime_thermal_throttle_act; /* 0x60 - Lifetime thermal throttle activations */
+ __le32 percentage_pe_cycles_remaining; /* 0x64 - Percentage of P/E cycles remaining */
+ __u8 rsvd[408]; /* 0x68 - 408 Reserved bytes */
+};
+
+#define WDC_OCP_C1_GUID_LENGTH 16
+#define WDC_ERROR_REC_LOG_BUF_LEN 512
+#define WDC_ERROR_REC_LOG_ID 0xC1
+#define WDC_ERROR_REC_LOG_VERSION1 0001
+#define WDC_ERROR_REC_LOG_VERSION2 0002
+
+struct __attribute__((__packed__)) wdc_ocp_c1_error_recovery_log {
+ __le16 panic_reset_wait_time; /* 000 - Panic Reset Wait Time */
+ __u8 panic_reset_action; /* 002 - Panic Reset Action */
+ __u8 dev_recovery_action1; /* 003 - Device Recovery Action 1 */
+ __le64 panic_id; /* 004 - Panic ID */
+ __le32 dev_capabilities; /* 012 - Device Capabilities */
+ __u8 vs_recovery_opc; /* 016 - Vendor Specific Recovery Opcode */
+ __u8 rsvd1[3]; /* 017 - 3 Reserved Bytes */
+ __le32 vs_cmd_cdw12; /* 020 - Vendor Specific Command CDW12 */
+ __le32 vs_cmd_cdw13; /* 024 - Vendor Specific Command CDW13 */
+ __u8 vs_cmd_to; /* 028 - Vendor Specific Command Timeout V2 */
+ __u8 dev_recovery_action2; /* 029 - Device Recovery Action 2 V2 */
+ __u8 dev_recovery_action2_to; /* 030 - Device Recovery Action 2 Timeout V2*/
+ __u8 rsvd2[463]; /* 031 - 463 Reserved Bytes */
+ __le16 log_page_version; /* 494 - Log Page Version */
+ __u8 log_page_guid[WDC_OCP_C1_GUID_LENGTH]; /* 496 - Log Page GUID */
+};
+
+static __u8 wdc_ocp_c1_guid[WDC_OCP_C1_GUID_LENGTH] = { 0x44, 0xD9, 0x31, 0x21, 0xFE, 0x30, 0x34, 0xAE,
+ 0xAB, 0x4D, 0xFD, 0x3D, 0xBA, 0x83, 0x19, 0x5A };
+
+/* NAND Stats */
+struct __attribute__((__packed__)) wdc_nand_stats {
+ __u8 nand_write_tlc[16];
+ __u8 nand_write_slc[16];
+ __le32 nand_prog_failure;
+ __le32 nand_erase_failure;
+ __le32 bad_block_count;
+ __le64 nand_rec_trigger_event;
+ __le64 e2e_error_counter;
+ __le64 successful_ns_resize_event;
+ __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 {
+ __le32 eye_catcher;
+ __u8 version;
+ __u8 reserved1;
+ __u8 num_entries;
+ __u8 reserved2;
+ __le32 entry_size;
+ __le32 reserved3;
+};
+
+struct wdc_fw_act_history_log_entry {
+ __le32 entry_num;
+ __le32 power_cycle_count;
+ __le64 power_on_seconds;
+ __le64 previous_fw_version;
+ __le64 new_fw_version;
+ __u8 slot_number;
+ __u8 commit_action_type;
+ __le16 result;
+ __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[WDC_MAX_NUM_ACT_HIST_ENTRIES];
+ __u8 reserved2[2790];
+ __le16 log_page_version;
+ __u8 log_page_guid[WDC_C2_GUID_LENGTH];
+};
+
+#define WDC_OCP_C4_GUID_LENGTH 16
+#define WDC_DEV_CAP_LOG_BUF_LEN 4096
+#define WDC_DEV_CAP_LOG_ID 0xC4
+#define WDC_DEV_CAP_LOG_VERSION 0001
+#define WDC_OCP_C4_NUM_PS_DESCR 127
+
+struct __attribute__((__packed__)) wdc_ocp_C4_dev_cap_log {
+ __le16 num_pcie_ports; /* 0000 - Number of PCI Express Ports */
+ __le16 oob_mgmt_support; /* 0002 - OOB Management Interfaces Supported */
+ __le16 wrt_zeros_support; /* 0004 - Write Zeros Commmand Support */
+ __le16 sanitize_support; /* 0006 - Sanitize Command Support */
+ __le16 dsm_support; /* 0008 - Dataset Management Command Support */
+ __le16 wrt_uncor_support; /* 0010 - Write Uncorrectable Command Support */
+ __le16 fused_support; /* 0012 - Fused Operation Support */
+ __le16 min_dssd_ps; /* 0014 - Minimum Valid DSSD Power State */
+ __u8 rsvd1; /* 0016 - Reserved must be cleared to zero */
+ __u8 dssd_ps_descr[WDC_OCP_C4_NUM_PS_DESCR];/* 0017 - DSSD Power State Descriptors */
+ __u8 rsvd2[3934]; /* 0144 - Reserved must be cleared to zero */
+ __le16 log_page_version; /* 4078 - Log Page Version */
+ __u8 log_page_guid[WDC_OCP_C4_GUID_LENGTH]; /* 4080 - Log Page GUID */
+};
+
+static __u8 wdc_ocp_c4_guid[WDC_OCP_C4_GUID_LENGTH] = { 0x97, 0x42, 0x05, 0x0D, 0xD1, 0xE1, 0xC9, 0x98,
+ 0x5D, 0x49, 0x58, 0x4B, 0x91, 0x3C, 0x05, 0xB7 };
+
+#define WDC_OCP_C5_GUID_LENGTH 16
+#define WDC_UNSUPPORTED_REQS_LOG_BUF_LEN 4096
+#define WDC_UNSUPPORTED_REQS_LOG_ID 0xC5
+#define WDC_UNSUPPORTED_REQS_LOG_VERSION 0001
+#define WDC_NUM_UNSUPPORTED_REQ_ENTRIES 253
+
+struct __attribute__((__packed__)) wdc_ocp_C5_unsupported_reqs {
+ __le16 unsupported_count; /* 0000 - Number of Unsupported Requirement IDs */
+ __u8 rsvd1[14]; /* 0002 - Reserved must be cleared to zero */
+ __u8 unsupported_req_list[WDC_NUM_UNSUPPORTED_REQ_ENTRIES][16]; /* 0016 - Unsupported Requirements List */
+ __u8 rsvd2[14]; /* 4064 - Reserved must be cleared to zero */
+ __le16 log_page_version; /* 4078 - Log Page Version */
+ __u8 log_page_guid[WDC_OCP_C5_GUID_LENGTH]; /* 4080 - Log Page GUID */
+};
+
+static __u8 wdc_ocp_c5_guid[WDC_OCP_C5_GUID_LENGTH] = { 0x2F, 0x72, 0x9C, 0x0E, 0x99, 0x23, 0x2C, 0xBB,
+ 0x63, 0x48, 0x32, 0xD0, 0xB7, 0x98, 0xBB, 0xC7 };
+
+#define WDC_REASON_INDEX_MAX 16
+#define WDC_REASON_ID_ENTRY_LEN 128
+#define WDC_REASON_ID_PATH_NAME "/usr/local/nvmecli"
+
+
+static double safe_div_fp(double numerator, double denominator)
+{
+ return denominator ? numerator / denominator : 0;
+}
+
+static double calc_percent(uint64_t numerator, uint64_t denominator)
+{
+ return denominator ?
+ (uint64_t)(((double)numerator / (double)denominator) * 100) : 0;
+}
+
+static int wdc_get_pci_ids(nvme_root_t r, struct nvme_dev *dev,
+ uint32_t *device_id, uint32_t *vendor_id)
+{
+ char vid[256], did[256], id[32];
+ nvme_ctrl_t c = NULL;
+ nvme_ns_t n = NULL;
+ int fd, ret;
+
+ c = nvme_scan_ctrl(r, dev->name);
+ if (c) {
+ snprintf(vid, sizeof(vid), "%s/device/vendor",
+ nvme_ctrl_get_sysfs_dir(c));
+ snprintf(did, sizeof(did), "%s/device/device",
+ nvme_ctrl_get_sysfs_dir(c));
+ nvme_free_ctrl(c);
+ } else {
+ n = nvme_scan_namespace(dev->name);
+ if (!n) {
+ fprintf(stderr, "Unable to find %s\n", dev->name);
+ return -1;
+ }
+
+ snprintf(vid, sizeof(vid), "%s/device/device/vendor",
+ nvme_ns_get_sysfs_dir(n));
+ snprintf(did, sizeof(did), "%s/device/device/device",
+ nvme_ns_get_sysfs_dir(n));
+ nvme_free_ns(n);
+ }
+
+ fd = open(vid, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR : WDC : %s : Open vendor file failed\n", __func__);
+ return -1;
+ }
+
+ ret = read(fd, id, 32);
+ close(fd);
+
+ if (ret < 0) {
+ fprintf(stderr, "%s: Read of pci vendor id failed\n", __func__);
+ return -1;
+ }
+ id[ret < 32 ? ret : 31] = '\0';
+ if (id[strlen(id) - 1] == '\n')
+ id[strlen(id) - 1] = '\0';
+
+ *vendor_id = strtol(id, NULL, 0);
+ ret = 0;
+
+ fd = open(did, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR : WDC : %s : Open device file failed\n", __func__);
+ return -1;
+ }
+
+ ret = read(fd, id, 32);
+ close(fd);
+
+ if (ret < 0) {
+ fprintf(stderr, "%s: Read of pci device id failed\n", __func__);
+ return -1;
+ }
+ id[ret < 32 ? ret : 31] = '\0';
+ if (id[strlen(id) - 1] == '\n')
+ id[strlen(id) - 1] = '\0';
+
+ *device_id = strtol(id, NULL, 0);
+ return 0;
+}
+
+static int wdc_get_vendor_id(struct nvme_dev *dev, uint32_t *vendor_id)
+{
+ int ret;
+ struct nvme_id_ctrl ctrl;
+
+ memset(&ctrl, 0, sizeof(struct nvme_id_ctrl));
+ ret = nvme_identify_ctrl(dev_fd(dev), &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(struct nvme_dev *dev, char *model)
+{
+ int ret,i;
+ struct nvme_id_ctrl ctrl;
+
+ memset(&ctrl, 0, sizeof(struct nvme_id_ctrl));
+ ret = nvme_identify_ctrl(dev_fd(dev), &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(nvme_root_t r, struct nvme_dev *dev)
+{
+ int ret;
+ bool supported;
+ uint32_t read_device_id = -1, read_vendor_id = -1;
+
+ ret = wdc_get_pci_ids(r, dev, &read_device_id, &read_vendor_id);
+ if (ret < 0) {
+ /* Use the identify nvme command to get vendor id due to NVMeOF device. */
+ if (wdc_get_vendor_id(dev, &read_vendor_id) < 0)
+ return false;
+ }
+
+ supported = false;
+
+ if (read_vendor_id == WDC_NVME_VID ||
+ read_vendor_id == WDC_NVME_VID_2 ||
+ read_vendor_id == WDC_NVME_SNDK_VID)
+ supported = true;
+ else
+ fprintf(stderr, "ERROR : WDC: unsupported WDC device, Vendor ID = 0x%x, Device ID = 0x%x\n",
+ read_vendor_id, read_device_id);
+
+ return supported;
+}
+
+static bool wdc_enc_check_model(struct nvme_dev *dev)
+{
+ int ret;
+ bool supported;
+ char model[NVME_ID_CTRL_MODEL_NUMBER_SIZE+1];
+
+ ret = wdc_get_model_number(dev, 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(nvme_root_t r, struct nvme_dev *dev)
+{
+ int ret;
+ uint32_t read_device_id = -1, read_vendor_id = -1;
+ __u64 capabilities = 0;
+ __u32 cust_id;
+
+ ret = wdc_get_pci_ids(r, dev, &read_device_id, &read_vendor_id);
+ if (ret < 0)
+ {
+ if (wdc_get_vendor_id(dev, &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(r, dev);
+ return capabilities;
+ }
+
+ switch (read_vendor_id) {
+ case WDC_NVME_VID:
+ switch (read_device_id) {
+ case WDC_NVME_SN100_DEV_ID:
+ capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_C1_LOG_PAGE |
+ WDC_DRIVE_CAP_DRIVE_LOG | WDC_DRIVE_CAP_CRASH_DUMP | WDC_DRIVE_CAP_PFAIL_DUMP |
+ WDC_DRIVE_CAP_PURGE);
+ break;
+ case WDC_NVME_SN200_DEV_ID:
+ 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 |
+ WDC_DRIVE_CAP_PURGE);
+
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE))
+ capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+ /* verify the 0xC1 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_ADD_LOG_OPCODE))
+ capabilities |= WDC_DRIVE_CAP_C1_LOG_PAGE;
+ break;
+ default:
+ capabilities = 0;
+ }
+ break;
+ case WDC_NVME_VID_2:
+ switch (read_device_id) {
+ case WDC_NVME_SN630_DEV_ID:
+ /* FALLTHRU */
+ case WDC_NVME_SN630_DEV_ID_1:
+ 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);
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE))
+ capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+ /* verify the 0xD0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_VU_SMART_LOG_OPCODE)
+ == true)
+ capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
+ break;
+ case WDC_NVME_SN640_DEV_ID:
+ /* FALLTHRU */
+ case WDC_NVME_SN640_DEV_ID_1:
+ /* FALLTHRU */
+ case WDC_NVME_SN640_DEV_ID_2:
+ /* FALLTHRU */
+ case WDC_NVME_SN640_DEV_ID_3:
+ /* FALLTHRU */
+ case WDC_NVME_SN560_DEV_ID_1:
+ /* FALLTHRU */
+ case WDC_NVME_SN560_DEV_ID_2:
+ /* FALLTHRU */
+ case WDC_NVME_SN560_DEV_ID_3:
+ /* FALLTHRU */
+ case WDC_NVME_SN660_DEV_ID:
+ /* verify the 0xC0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID)
+ == 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 0xC1 (OCP Error Recovery) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_ERROR_REC_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_OCP_C1_LOG_PAGE;
+
+ /* verify the 0xC3 (OCP Latency Monitor) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_LATENCY_MON_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_C3_LOG_PAGE;
+
+ /* verify the 0xC4 (OCP Device Capabilities) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_DEV_CAP_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_OCP_C4_LOG_PAGE;
+
+ /* verify the 0xC5 (OCP Unsupported Requirments) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_UNSUPPORTED_REQS_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_OCP_C5_LOG_PAGE;
+
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE))
+ capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+ /* verify the 0xD0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_VU_SMART_LOG_OPCODE))
+ capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
+
+ cust_id = wdc_get_fw_cust_id(r, dev);
+ if (cust_id == WDC_INVALID_CUSTOMER_ID) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid customer id\n", __func__);
+ return -1;
+ }
+
+ 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:
+ /* FALLTHRU */
+ case WDC_NVME_SN860_DEV_ID:
+ /* verify the 0xC0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_EOL_STATUS_LOG_OPCODE))
+ capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE;
+ /* FALLTHRU */
+ case WDC_NVME_ZN540_DEV_ID:
+ /* FALLTHRU */
+ 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 );
+
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE))
+ capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+ /* verify the 0xD0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_VU_SMART_LOG_OPCODE))
+ 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:
+ case WDC_NVME_SN650_DEV_ID_4:
+ case WDC_NVME_SN655_DEV_ID:
+ case WDC_NVME_SN550_DEV_ID:
+ /* verify the 0xC0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_C0_LOG_PAGE;
+
+ /* verify the 0xC1 (OCP Error Recovery) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_ERROR_REC_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_OCP_C1_LOG_PAGE;
+
+ /* verify the 0xC3 (OCP Latency Monitor) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_LATENCY_MON_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_C3_LOG_PAGE;
+
+ /* verify the 0xC4 (OCP Device Capabilities) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_DEV_CAP_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_OCP_C4_LOG_PAGE;
+
+ /* verify the 0xC5 (OCP Unsupported Requirments) log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_UNSUPPORTED_REQS_LOG_ID))
+ capabilities |= WDC_DRIVE_CAP_OCP_C5_LOG_PAGE;
+
+ capabilities |= (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG |
+ WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT |
+ WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY |
+ WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID |
+ WDC_DRIVE_CAP_LOG_PAGE_DIR);
+
+ cust_id = wdc_get_fw_cust_id(r, dev);
+ if (cust_id == WDC_INVALID_CUSTOMER_ID) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid customer id\n", __func__);
+ return -1;
+ }
+
+ 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;
+ default:
+ capabilities = 0;
+ }
+ break;
+ case WDC_NVME_SNDK_VID:
+ switch (read_device_id) {
+ case WDC_NVME_SXSLCL_DEV_ID:
+ capabilities = WDC_DRIVE_CAP_DRIVE_ESSENTIALS;
+ break;
+ case WDC_NVME_SN520_DEV_ID:
+ /* FALLTHRU */
+ case WDC_NVME_SN520_DEV_ID_1:
+ /* FALLTHRU */
+ case WDC_NVME_SN520_DEV_ID_2:
+ case WDC_NVME_SN530_DEV_ID:
+ case WDC_NVME_SN810_DEV_ID:
+ capabilities = WDC_DRIVE_CAP_DUI_DATA;
+ break;
+ case WDC_NVME_SN820CL_DEV_ID:
+ capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION |
+ WDC_DRIVE_CAP_CLOUD_LOG_PAGE | WDC_DRIVE_CAP_C0_LOG_PAGE |
+ WDC_DRIVE_CAP_HW_REV_LOG_PAGE | WDC_DRIVE_CAP_INFO |
+ WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_NAND_STATS |
+ WDC_DRIVE_CAP_DEVICE_WAF | WDC_DRIVE_CAP_TEMP_STATS;
+ break;
+ case WDC_NVME_SN720_DEV_ID:
+ 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 | WDC_DRIVE_CAP_NAND_STATS | WDC_DRIVE_CAP_INFO |
+ WDC_DRIVE_CAP_TEMP_STATS | WDC_DRIVE_CAP_VUC_CLEAR_PCIE | WDC_DRIVE_CAP_PCIE_STATS;
+ break;
+ case WDC_NVME_SN740_DEV_ID:
+ case WDC_NVME_SN740_DEV_ID_1:
+ case WDC_NVME_SN740_DEV_ID_2:
+ case WDC_NVME_SN740_DEV_ID_3:
+ case WDC_NVME_SN340_DEV_ID:
+ 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;
+ }
+ break;
+ default:
+ capabilities = 0;
+ }
+
+ return capabilities;
+}
+
+static __u64 wdc_get_enc_drive_capabilities(nvme_root_t r,
+ struct nvme_dev *dev)
+{
+ int ret;
+ uint32_t read_vendor_id;
+ __u64 capabilities = 0;
+ __u32 cust_id;
+
+ ret = wdc_get_vendor_id(dev, &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(r, dev, 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(r, dev, 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 0xC3 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_LATENCY_MON_LOG_ID) == true)
+ capabilities |= WDC_DRIVE_CAP_C3_LOG_PAGE;
+
+ /* verify the 0xCB log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, 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(r, dev, 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(r, dev, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
+
+ cust_id = wdc_get_fw_cust_id(r, dev);
+ if (cust_id == WDC_INVALID_CUSTOMER_ID) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid customer id\n", __func__);
+ return -1;
+ }
+
+ 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(struct nvme_dev *dev, char *file, size_t len,
+ const char *suffix)
+{
+ int i;
+ int ret;
+ int res_len = 0;
+ char orig[PATH_MAX] = {0};
+ struct nvme_id_ctrl ctrl;
+ int ctrl_sn_len = sizeof (ctrl.sn);
+
+ i = sizeof (ctrl.sn) - 1;
+ strncpy(orig, file, PATH_MAX - 1);
+ memset(file, 0, len);
+ memset(&ctrl, 0, sizeof (struct nvme_id_ctrl));
+ ret = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed "
+ "0x%x\n", ret);
+ return -1;
+ }
+ /* Remove trailing spaces from the name */
+ while (i && ctrl.sn[i] == ' ') {
+ ctrl.sn[i] = '\0';
+ i--;
+ }
+ if (ctrl.sn[sizeof (ctrl.sn) - 1] == '\0') {
+ ctrl_sn_len = strlen(ctrl.sn);
+ }
+
+ res_len = snprintf(file, len, "%s%.*s%s", orig, ctrl_sn_len, ctrl.sn, suffix);
+ if (len <= res_len) {
+ fprintf(stderr, "ERROR : WDC : cannot format serial number due to data "
+ "of unexpected length\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int wdc_create_log_file(char *file, __u8 *drive_log_data,
+ __u32 drive_log_length)
+{
+ int fd;
+ int ret;
+
+ if (drive_log_length == 0) {
+ fprintf(stderr, "ERROR : WDC: invalid log file length\n");
+ return -1;
+ }
+
+ fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR : WDC: open : %s\n", strerror(errno));
+ return -1;
+ }
+
+ while (drive_log_length > WRITE_SIZE) {
+ ret = write(fd, drive_log_data, WRITE_SIZE);
+ if (ret < 0) {
+ fprintf (stderr, "ERROR : WDC: write : %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ drive_log_data += WRITE_SIZE;
+ drive_log_length -= WRITE_SIZE;
+ }
+
+ ret = write(fd, drive_log_data, drive_log_length);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR : WDC : write : %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (fsync(fd) < 0) {
+ fprintf(stderr, "ERROR : WDC : fsync : %s\n", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ 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(nvme_root_t r, struct nvme_dev *dev,
+ __u8 log_id, void **cbs_data)
+{
+ int ret = -1;
+ void* data;
+ struct wdc_c2_log_page_header *hdr_ptr;
+ 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(r, dev, &device_id, &read_vendor_id);
+ if(device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) {
+ lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8;
+ uuid_ix = 0;
+ } else
+ lid = WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE;
+
+ if ((data = (__u8*) malloc(sizeof (__u8) * WDC_C2_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return false;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_C2_LOG_BUF_LEN);
+
+ /* get the log page length */
+ struct nvme_get_log_args args_len = {
+ .args_size = sizeof(args_len),
+ .fd = dev_fd(dev),
+ .lid = lid,
+ .nsid = 0xFFFFFFFF,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_ix,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = WDC_C2_LOG_BUF_LEN,
+ .log = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args_len);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : Unable to get 0x%x Log Page length, ret = 0x%x\n", lid, ret);
+ goto end;
+ }
+
+ hdr_ptr = (struct wdc_c2_log_page_header *)data;
+ length = le32_to_cpu(hdr_ptr->length);
+
+ if (length > WDC_C2_LOG_BUF_LEN) {
+ /* Log Page buffer too small, free and reallocate the necessary size */
+ free(data);
+ data = calloc(length, sizeof(__u8));
+ if (data == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return false;
+ }
+ }
+
+ /* get the log page data */
+ struct nvme_get_log_args args_data = {
+ .args_size = sizeof(args_data),
+ .fd = dev_fd(dev),
+ .lid = lid,
+ .nsid = 0xFFFFFFFF,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_ix,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = length,
+ .log = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args_data);
+
+ if (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(le32_to_cpu(hdr_ptr->length), log_id, hdr_ptr, &sph);
+ if (found) {
+ *cbs_data = calloc(le32_to_cpu(sph->length), sizeof(__u8));
+ if (*cbs_data == NULL) {
+ fprintf(stderr, "ERROR : WDC : calloc : %s\n", strerror(errno));
+ goto end;
+ }
+ memcpy((void *)*cbs_data, (void *)&sph->data, le32_to_cpu(sph->length));
+ } else {
+ /* not found with uuid = 1 try with uuid = 0 */
+ uuid_ix = 0;
+ /* get the log page data */
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = lid,
+ .nsid = 0xFFFFFFFF,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_ix,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = le32_to_cpu(hdr_ptr->length),
+ .log = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args);
+
+ hdr_ptr = (struct wdc_c2_log_page_header *)data;
+ sph = (struct wdc_c2_log_subpage_header *)(data + length);
+ found = wdc_get_dev_mng_log_entry(le32_to_cpu(hdr_ptr->length), log_id, hdr_ptr, &sph);
+ if (found) {
+ *cbs_data = calloc(le32_to_cpu(sph->length), sizeof(__u8));
+ if (*cbs_data == NULL) {
+ fprintf(stderr, "ERROR : WDC : calloc : %s\n", strerror(errno));
+ goto end;
+ }
+ memcpy((void *)*cbs_data, (void *)&sph->data, le32_to_cpu(sph->length));
+
+ } 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);
+ }
+ }
+end:
+ free(data);
+ return found;
+}
+
+static bool wdc_nvme_check_supported_log_page(nvme_root_t r,
+ struct nvme_dev *dev,
+ __u8 log_id)
+{
+ int i;
+ bool found = false;
+ struct wdc_c2_cbs_data *cbs_data = NULL;
+
+ if (get_dev_mgment_cbs_data(r, dev, WDC_C2_LOG_PAGES_SUPPORTED_ID, (void *)&cbs_data)) {
+ if (cbs_data != NULL) {
+ for (i = 0; i < le32_to_cpu(cbs_data->length); i++) {
+ if (log_id == cbs_data->data[i]) {
+ found = true;
+ break;
+ }
+ }
+
+#ifdef WDC_NVME_CLI_DEBUG
+ if (!found) {
+ fprintf(stderr, "ERROR : WDC : Log Page 0x%x not supported\n", log_id);
+ fprintf(stderr, "WDC : Supported Log Pages:\n");
+ /* print the supported pages */
+ d((__u8 *)cbs_data->data, le32_to_cpu(cbs_data->length), 16, 1);
+ }
+#endif
+ free(cbs_data);
+ } else
+ fprintf(stderr, "ERROR : WDC : cbs_data ptr = NULL\n");
+ } else
+ fprintf(stderr, "ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", WDC_C2_LOG_PAGES_SUPPORTED_ID);
+
+ return found;
+}
+
+static bool wdc_nvme_get_dev_status_log_data(nvme_root_t r,
+ struct nvme_dev *dev,
+ __le32 *ret_data,
+ __u8 log_id)
+{
+ __u32 *cbs_data = NULL;
+
+ if (get_dev_mgment_cbs_data(r, dev, log_id, (void *)&cbs_data)) {
+ if (cbs_data != NULL) {
+ memcpy((void *)ret_data, (void *)cbs_data, 4);
+ free(cbs_data);
+
+ return true;
+ }
+ }
+
+ *ret_data = 0;
+ return false;
+}
+
+static int wdc_do_clear_dump(struct nvme_dev *dev, __u8 opcode, __u32 cdw12)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = opcode;
+ admin_cmd.cdw12 = cdw12;
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+ if (ret != 0) {
+ fprintf(stdout, "ERROR : WDC : Crash dump erase failed\n");
+ }
+ nvme_show_status(ret);
+ return ret;
+}
+
+static __u32 wdc_dump_length(int fd, __u32 opcode, __u32 cdw10, __u32 cdw12, __u32 *dump_length)
+{
+ int ret;
+ __u8 buf[WDC_NVME_LOG_SIZE_DATA_LEN] = {0};
+ struct wdc_log_size *l;
+ struct nvme_passthru_cmd admin_cmd;
+
+ l = (struct wdc_log_size *) buf;
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = opcode;
+ admin_cmd.addr = (__u64)(uintptr_t)buf;
+ admin_cmd.data_len = WDC_NVME_LOG_SIZE_DATA_LEN;
+ admin_cmd.cdw10 = cdw10;
+ admin_cmd.cdw12 = cdw12;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL);
+ if (ret != 0) {
+ l->log_size = 0;
+ ret = -1;
+ fprintf(stderr, "ERROR : WDC : reading dump length failed\n");
+ nvme_show_status(ret);
+ return ret;
+ }
+
+ if (opcode == WDC_NVME_CAP_DIAG_OPCODE)
+ *dump_length = buf[0x04] << 24 | buf[0x05] << 16 | buf[0x06] << 8 | buf[0x07];
+ else
+ *dump_length = le32_to_cpu(l->log_size);
+ return ret;
+}
+
+static __u32 wdc_dump_length_e6(int fd, __u32 opcode, __u32 cdw10, __u32 cdw12, struct wdc_e6_log_hdr *dump_hdr)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = opcode;
+ admin_cmd.addr = (__u64)(uintptr_t)dump_hdr;
+ admin_cmd.data_len = WDC_NVME_LOG_SIZE_HDR_LEN;
+ admin_cmd.cdw10 = cdw10;
+ admin_cmd.cdw12 = cdw12;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : reading dump length failed\n");
+ nvme_show_status(ret);
+ }
+
+ return ret;
+}
+
+static __u32 wdc_dump_dui_data(int fd, __u32 dataLen, __u32 offset, __u8 *dump_data, bool last_xfer)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = WDC_NVME_CAP_DUI_OPCODE;
+ admin_cmd.nsid = 0xFFFFFFFF;
+ admin_cmd.addr = (__u64)(uintptr_t)dump_data;
+ admin_cmd.data_len = dataLen;
+ admin_cmd.cdw10 = ((dataLen >> 2) - 1);
+ admin_cmd.cdw12 = offset;
+ if (last_xfer)
+ admin_cmd.cdw14 = 0;
+ else
+ admin_cmd.cdw14 = WDC_NVME_CAP_DUI_DISABLE_IO;
+
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : reading DUI data failed\n");
+ nvme_show_status(ret);
+ }
+
+ return ret;
+}
+
+static __u32 wdc_dump_dui_data_v2(int fd, __u32 dataLen, __u64 offset, __u8 *dump_data, bool last_xfer)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+ __u64 offset_lo, offset_hi;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = WDC_NVME_CAP_DUI_OPCODE;
+ admin_cmd.nsid = 0xFFFFFFFF;
+ admin_cmd.addr = (__u64)(uintptr_t)dump_data;
+ admin_cmd.data_len = dataLen;
+ admin_cmd.cdw10 = ((dataLen >> 2) - 1);
+ 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
+ admin_cmd.cdw14 = WDC_NVME_CAP_DUI_DISABLE_IO;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : reading DUI data V2 failed\n");
+ nvme_show_status(ret);
+ }
+
+ return ret;
+}
+
+static int wdc_do_dump(struct nvme_dev *dev, __u32 opcode,__u32 data_len,
+ __u32 cdw12, char *file, __u32 xfer_size)
+{
+ int ret = 0;
+ __u8 *dump_data;
+ __u32 curr_data_offset, curr_data_len;
+ int i;
+ struct nvme_passthru_cmd admin_cmd;
+ __u32 dump_length = data_len;
+
+ 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_passthru_cmd));
+ curr_data_offset = 0;
+ curr_data_len = xfer_size;
+ i = 0;
+
+ admin_cmd.opcode = opcode;
+ admin_cmd.addr = (__u64)(uintptr_t)dump_data;
+ admin_cmd.data_len = curr_data_len;
+ admin_cmd.cdw10 = curr_data_len >> 2;
+ admin_cmd.cdw12 = cdw12;
+ admin_cmd.cdw13 = curr_data_offset;
+
+ while (curr_data_offset < data_len) {
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd,
+ NULL);
+ if (ret != 0) {
+ nvme_show_status(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;
+ admin_cmd.addr = (__u64)(uintptr_t)dump_data + (__u64)curr_data_offset;
+ admin_cmd.data_len = curr_data_len;
+ admin_cmd.cdw10 = curr_data_len >> 2;
+ admin_cmd.cdw13 = curr_data_offset >> 2;
+ i++;
+ }
+
+ if (ret == 0) {
+ nvme_show_status(ret);
+ ret = wdc_create_log_file(file, dump_data, dump_length);
+ }
+ free(dump_data);
+ return ret;
+}
+
+static int wdc_do_dump_e6(int fd, __u32 opcode,__u32 data_len,
+ __u32 cdw12, char *file, __u32 xfer_size, __u8 *log_hdr)
+{
+ int ret = 0;
+ __u8 *dump_data;
+ __u32 curr_data_offset, log_size;
+ int i;
+ struct nvme_passthru_cmd admin_cmd;
+
+ dump_data = (__u8 *) malloc(sizeof (__u8) * data_len);
+
+ if (dump_data == NULL) {
+ fprintf(stderr, "%s: ERROR : malloc : %s\n", __func__, strerror(errno));
+ return -1;
+ }
+ memset(dump_data, 0, sizeof (__u8) * data_len);
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ curr_data_offset = WDC_NVME_LOG_SIZE_HDR_LEN;
+ i = 0;
+
+ /* copy the 8 byte header into the dump_data buffer */
+ memcpy(dump_data, log_hdr, WDC_NVME_LOG_SIZE_HDR_LEN);
+
+ admin_cmd.opcode = opcode;
+ admin_cmd.cdw12 = cdw12;
+
+ log_size = data_len;
+ while (log_size > 0) {
+ xfer_size = min(xfer_size, log_size);
+
+ admin_cmd.addr = (__u64)(uintptr_t)dump_data + (__u64)curr_data_offset;
+ admin_cmd.data_len = xfer_size;
+ admin_cmd.cdw10 = xfer_size >> 2;
+ admin_cmd.cdw13 = curr_data_offset >> 2;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL);
+ if (ret != 0) {
+ nvme_show_status(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;
+ }
+
+ log_size -= xfer_size;
+ curr_data_offset += xfer_size;
+ i++;
+ }
+
+ if (ret == 0) {
+ fprintf(stderr, "%s: ", __func__);
+ nvme_show_status(ret);
+ } else {
+ fprintf(stderr, "%s: FAILURE: ", __func__);
+ nvme_show_status(ret);
+ fprintf(stderr, "%s: Partial data may have been captured\n", __func__);
+ snprintf(file + strlen(file), PATH_MAX, "%s", "-PARTIAL");
+ }
+
+ ret = wdc_create_log_file(file, dump_data, data_len);
+
+ free(dump_data);
+ return ret;
+}
+
+static int wdc_do_cap_telemetry_log(struct nvme_dev *dev, char *file,
+ __u32 bs, int type, int data_area)
+{
+ struct nvme_telemetry_log *log;
+ size_t full_size = 0;
+ int err = 0, output;
+ __u32 host_gen = 1;
+ int ctrl_init = 0;
+ __u32 result;
+ void *buf = NULL;
+ __u8 *data_ptr = NULL;
+ int data_written = 0, data_remaining = 0;
+ struct nvme_id_ctrl ctrl;
+ __u64 capabilities = 0;
+ nvme_root_t r;
+
+ memset(&ctrl, 0, sizeof (struct nvme_id_ctrl));
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (err) {
+ fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed "
+ "0x%x\n", err);
+ return err;
+ }
+
+ if (!(ctrl.lpa & 0x8)) {
+ fprintf(stderr, "Telemetry Host-Initiated and Telemetry Controller-Initiated log pages not supported\n");
+ return -EINVAL;
+ }
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if (type == WDC_TELEMETRY_TYPE_HOST) {
+ host_gen = 1;
+ ctrl_init = 0;
+ } else if (type == WDC_TELEMETRY_TYPE_CONTROLLER) {
+ if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) {
+ /* Verify the Controller Initiated Option is enabled */
+ err = nvme_get_features_data(dev_fd(dev),
+ WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID,
+ 0, 4, buf, &result);
+ if (err == 0) {
+ if (result == 0) {
+ /* enabled */
+ host_gen = 0;
+ ctrl_init = 1;
+ }
+ else {
+ fprintf(stderr, "%s: Controller initiated option telemetry log page disabled\n", __func__);
+ return -EINVAL;
+ }
+ }
+ else {
+ fprintf(stderr, "ERROR : WDC: Get telemetry option feature failed.");
+ nvme_show_status(err);
+ return -EPERM;
+ }
+ }
+ else {
+ host_gen = 0;
+ ctrl_init = 1;
+ }
+ } else {
+ fprintf(stderr, "%s: Invalid type parameter; type = %d\n", __func__, type);
+ return -EINVAL;
+ }
+
+ if (!file) {
+ fprintf(stderr, "%s: Please provide an output file!\n", __func__);
+ return -EINVAL;
+ }
+
+ 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));
+ return output;
+ }
+
+ if (ctrl_init)
+ err = nvme_get_ctrl_telemetry(dev_fd(dev), true, &log,
+ data_area, &full_size);
+ else if (host_gen)
+ err = nvme_get_new_host_telemetry(dev_fd(dev), &log,
+ data_area, &full_size);
+ else
+ err = nvme_get_host_telemetry(dev_fd(dev), &log, data_area,
+ &full_size);
+
+ if (err < 0) {
+ perror("get-telemetry-log");
+ goto close_output;
+ } else if (err > 0) {
+ nvme_show_status(err);
+ fprintf(stderr, "%s: Failed to acquire telemetry header!\n", __func__);
+ goto close_output;
+ }
+
+ /*
+ * Continuously pull data until the offset hits the end of the last
+ * block.
+ */
+ data_written = 0;
+ data_remaining = full_size;
+ data_ptr = (__u8 *)log;
+
+ while (data_remaining) {
+ data_written = write(output, data_ptr, data_remaining);
+
+ if (data_written < 0) {
+ data_remaining = data_written;
+ break;
+ } else if (data_written <= data_remaining) {
+ data_remaining -= data_written;
+ data_ptr += data_written;
+ } else {
+ /* Unexpected overwrite */
+ fprintf(stderr, "Failure: Unexpected telemetry log overwrite - data_remaining = 0x%x, data_written = 0x%x\n",
+ data_remaining, data_written);
+ break;
+ }
+ }
+
+ if (fsync(output) < 0) {
+ fprintf(stderr, "ERROR : %s: fsync : %s\n", __func__, strerror(errno));
+ err = -1;
+ }
+
+ free(log);
+close_output:
+ close(output);
+ return err;
+}
+
+static int wdc_do_cap_diag(nvme_root_t r, struct nvme_dev *dev, char *file,
+ __u32 xfer_size, int type, int data_area)
+{
+ int ret = -1;
+ __u32 e6_log_hdr_size = WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE;
+ struct wdc_e6_log_hdr *log_hdr;
+ __u32 cap_diag_length;
+
+ log_hdr = (struct wdc_e6_log_hdr *) malloc(e6_log_hdr_size);
+ if (log_hdr == NULL) {
+ fprintf(stderr, "%s: ERROR : malloc : %s\n", __func__, strerror(errno));
+ ret = -1;
+ goto out;
+ }
+ memset(log_hdr, 0, e6_log_hdr_size);
+
+ if (type == WDC_TELEMETRY_TYPE_NONE) {
+ ret = wdc_dump_length_e6(dev_fd(dev),
+ WDC_NVME_CAP_DIAG_OPCODE,
+ WDC_NVME_CAP_DIAG_HEADER_TOC_SIZE>>2,
+ 0x00,
+ log_hdr);
+ if (ret == -1) {
+ ret = -1;
+ goto out;
+ }
+
+ cap_diag_length = (log_hdr->log_size[0] << 24 | log_hdr->log_size[1] << 16 |
+ log_hdr->log_size[2] << 8 | log_hdr->log_size[3]);
+
+ if (cap_diag_length == 0) {
+ fprintf(stderr, "INFO : WDC : Capture Diagnostics log is empty\n");
+ } else {
+ ret = wdc_do_dump_e6(dev_fd(dev),
+ WDC_NVME_CAP_DIAG_OPCODE,
+ cap_diag_length,
+ (WDC_NVME_CAP_DIAG_SUBCMD << WDC_NVME_SUBCMD_SHIFT) | WDC_NVME_CAP_DIAG_CMD,
+ file, xfer_size, (__u8 *)log_hdr);
+
+ fprintf(stderr, "INFO : WDC : Capture Diagnostics log, length = 0x%x\n", cap_diag_length);
+ }
+ } else if ((type == WDC_TELEMETRY_TYPE_HOST) ||
+ (type == WDC_TELEMETRY_TYPE_CONTROLLER)) {
+ /* Get the desired telemetry log page */
+ ret = wdc_do_cap_telemetry_log(dev, file, xfer_size, type, data_area);
+ } else
+ fprintf(stderr, "%s: ERROR : Invalid type : %d\n", __func__, type);
+
+out:
+ free(log_hdr);
+ return ret;
+}
+
+static int wdc_do_cap_dui(int fd, char *file, __u32 xfer_size, int data_area, int verbose, __u64 file_size, __u64 offset)
+{
+ int ret = 0;
+ __u32 dui_log_hdr_size = WDC_NVME_CAP_DUI_HEADER_SIZE;
+ struct wdc_dui_log_hdr *log_hdr;
+ 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;
+ int i;
+ int j;
+ bool last_xfer = false;
+ int err = 0, output = 0;
+
+ log_hdr = (struct wdc_dui_log_hdr *) malloc(dui_log_hdr_size);
+ if (log_hdr == NULL) {
+ fprintf(stderr, "%s: ERROR : log header malloc failed : status %s, size 0x%x\n",
+ __func__, strerror(errno), dui_log_hdr_size);
+ return -1;
+ }
+ memset(log_hdr, 0, dui_log_hdr_size);
+
+ /* get the dui telemetry and log headers */
+ ret = wdc_dump_dui_data(fd, WDC_NVME_CAP_DUI_HEADER_SIZE, 0x00, (__u8 *)log_hdr, last_xfer);
+ if (ret != 0) {
+ fprintf(stderr, "%s: ERROR : WDC : Get DUI headers failed\n", __func__);
+ fprintf(stderr, "%s: ERROR : WDC : ", __func__);
+ nvme_show_status(ret);
+ goto out;
+ }
+
+ /* Check the Log Header version */
+ 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 : ",
+ __func__);
+ nvme_show_status(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;
+ __u64 xfer_size_long = (__u64)xfer_size;
+
+ log_hdr_v3 = (struct wdc_dui_log_hdr_v3 *)log_hdr;
+
+ cap_dui_length_v3 = le64_to_cpu(log_hdr_v3->log_size);
+
+ if (verbose) {
+ 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->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) {
+ fprintf(stderr, "INFO : WDC : Capture V2 or V3 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_V3; j++) {
+ if (log_hdr_v3->log_section[j].data_area_id <= data_area &&
+ 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%"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%"PRIx64"\n", __func__, (uint64_t)log_size);
+ break;
+ }
+ }
+ } else
+ log_size = cap_dui_length_v3;
+
+ total_size = log_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 v3 malloc failed : status %s, size = 0x%"PRIx64"\n",
+ __func__, strerror(errno), (uint64_t)xfer_size_long);
+ ret = -1;
+ goto out;
+ }
+ memset(dump_data, 0, sizeof (__u8) * xfer_size_long);
+
+ 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;
+ }
+
+ 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;
+
+ }
+
+ i = 0;
+ buffer_addr = dump_data;
+
+ for(; log_size > 0; log_size -= xfer_size_long) {
+ xfer_size_long = min(xfer_size_long, log_size);
+
+ if (log_size <= xfer_size_long)
+ last_xfer = true;
+
+ 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%"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 : ", __func__);
+ nvme_show_status(ret);
+ break;
+ }
+
+ /* 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%"PRIx64"\n",
+ __func__, i, err, (uint64_t)xfer_size_long);
+ goto free_mem;
+ }
+
+ curr_data_offset += xfer_size_long;
+ i++;
+ }
+ }
+ }
+ 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_v4 = le64_to_cpu(log_hdr_v4->log_size_sectors) * WDC_NVME_SN730_SECTOR_SIZE;
+
+ if (verbose) {
+ 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_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_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 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%"PRIx64"\n", __func__, (uint64_t)log_size);
+ break;
+ }
+ }
+ } else
+ log_size = cap_dui_length_v4;
+
+ total_size = log_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 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_long);
+
+ 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;
+ }
+
+ 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;
+
+ }
+
+ i = 0;
+ buffer_addr = dump_data;
+
+ for(; log_size > 0; log_size -= xfer_size_long) {
+ xfer_size_long = min(xfer_size_long, log_size);
+
+ if (log_size <= xfer_size_long)
+ last_xfer = true;
+
+ 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%"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 :", __func__);
+ nvme_show_status(ret);
+ break;
+ }
+
+ /* 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_long = 0x%"PRIx64"\n",
+ __func__, i, err, (uint64_t)xfer_size_long);
+ goto free_mem;
+ }
+
+ curr_data_offset += xfer_size_long;
+ i++;
+ }
+ }
+ }
+ else {
+ fprintf(stderr, "INFO : WDC : Unsupported header version = 0x%x\n", log_hdr->hdr_version);
+ goto out;
+ }
+
+ nvme_show_status(ret);
+ if (verbose)
+ fprintf(stderr, "INFO : WDC : Capture Device Unit Info log, length = 0x%"PRIx64"\n", (uint64_t)total_size);
+
+ free_mem:
+ close(output);
+ free(dump_data);
+
+ out:
+ free(log_hdr);
+ return ret;
+}
+
+static int wdc_cap_diag(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ nvme_root_t r;
+ char *desc = "Capture Diagnostics Log.";
+ char *file = "Output file pathname.";
+ char *size = "Data retrieval transfer size.";
+ __u64 capabilities = 0;
+ char f[PATH_MAX] = {0};
+ struct nvme_dev *dev;
+ __u32 xfer_size = 0;
+ int ret = 0;
+
+ struct config {
+ char *file;
+ __u32 xfer_size;
+ };
+
+ struct config cfg = {
+ .file = NULL,
+ .xfer_size = 0x10000
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FILE("output-file", 'o', &cfg.file, file),
+ OPT_UINT("transfer-size", 's', &cfg.xfer_size, size),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ if (cfg.file != NULL)
+ strncpy(f, cfg.file, PATH_MAX - 1);
+ if (cfg.xfer_size != 0)
+ xfer_size = cfg.xfer_size;
+ ret = wdc_get_serial_name(dev, f, PATH_MAX, "cap_diag");
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC: failed to generate file name\n");
+ goto out;
+ }
+ if (cfg.file == NULL) {
+ if (strlen(f) > PATH_MAX - 5) {
+ fprintf(stderr, "ERROR : WDC: file name overflow\n");
+ ret = -1;
+ goto out;
+ }
+ strcat(f, ".bin");
+ }
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_CAP_DIAG) == WDC_DRIVE_CAP_CAP_DIAG)
+ ret = wdc_do_cap_diag(r, dev, f, xfer_size, 0, 0);
+ else
+ fprintf(stderr,
+ "ERROR : WDC: unsupported device for this command\n");
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_do_get_sn730_log_len(int fd, uint32_t *len_buf, uint32_t subopcode)
+{
+ int ret;
+ uint32_t *output = NULL;
+ struct nvme_passthru_cmd admin_cmd;
+
+ if ((output = (uint32_t*)malloc(sizeof(uint32_t))) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(output, 0, sizeof (uint32_t));
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+
+ admin_cmd.data_len = 8;
+ admin_cmd.opcode = SN730_NVME_GET_LOG_OPCODE;
+ admin_cmd.addr = (uintptr_t)output;
+ admin_cmd.cdw12 = subopcode;
+ admin_cmd.cdw10 = SN730_LOG_CHUNK_SIZE / 4;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL);
+ if (ret == 0)
+ *len_buf = *output;
+ free(output);
+ return ret;
+}
+
+static int wdc_do_get_sn730_log(int fd, void * log_buf, uint32_t offset, uint32_t subopcode)
+{
+ int ret;
+ uint8_t *output = NULL;
+ struct nvme_passthru_cmd admin_cmd;
+
+ if ((output = (uint8_t*)calloc(SN730_LOG_CHUNK_SIZE, sizeof(uint8_t))) == NULL) {
+ fprintf(stderr, "ERROR : WDC : calloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.data_len = SN730_LOG_CHUNK_SIZE;
+ admin_cmd.opcode = SN730_NVME_GET_LOG_OPCODE;
+ admin_cmd.addr = (uintptr_t)output;
+ admin_cmd.cdw12 = subopcode;
+ admin_cmd.cdw13 = offset;
+ admin_cmd.cdw10 = SN730_LOG_CHUNK_SIZE / 4;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd, NULL);
+ if (!ret)
+ memcpy(log_buf, output, SN730_LOG_CHUNK_SIZE);
+ return ret;
+}
+
+static int get_sn730_log_chunks(int fd, uint8_t* log_buf, uint32_t log_len, uint32_t subopcode)
+{
+ int ret = 0;
+ uint8_t* chunk_buf = NULL;
+ int remaining = log_len;
+ int curr_offset = 0;
+
+ if ((chunk_buf = (uint8_t*) malloc(sizeof (uint8_t) * SN730_LOG_CHUNK_SIZE)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ ret = -1;
+ goto out;
+ }
+
+ while (remaining > 0) {
+ memset(chunk_buf, 0, SN730_LOG_CHUNK_SIZE);
+ ret = wdc_do_get_sn730_log(fd, chunk_buf, curr_offset, subopcode);
+ if (!ret) {
+ if (remaining >= SN730_LOG_CHUNK_SIZE) {
+ memcpy(log_buf + (curr_offset * SN730_LOG_CHUNK_SIZE),
+ chunk_buf, SN730_LOG_CHUNK_SIZE);
+ } else {
+ memcpy(log_buf + (curr_offset * SN730_LOG_CHUNK_SIZE),
+ chunk_buf, remaining);
+ }
+ remaining -= SN730_LOG_CHUNK_SIZE;
+ curr_offset += 1;
+ } else
+ goto out;
+ }
+out:
+ free(chunk_buf);
+ return ret;
+}
+
+static int wdc_do_sn730_get_and_tar(int fd, char * outputName)
+{
+ int ret = 0;
+ void *retPtr;
+ uint8_t* full_log_buf = NULL;
+ uint8_t* key_log_buf = NULL;
+ uint8_t* core_dump_log_buf = NULL;
+ uint8_t* extended_log_buf = NULL;
+ uint32_t full_log_len = 0;
+ uint32_t key_log_len = 0;
+ uint32_t core_dump_log_len = 0;
+ uint32_t extended_log_len = 0;
+ tarfile_metadata* tarInfo = NULL;
+
+ tarInfo = (struct tarfile_metadata*) malloc(sizeof(tarfile_metadata));
+ if (tarInfo == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ ret = -1;
+ goto free_buf;
+ }
+ memset(tarInfo, 0, sizeof(tarfile_metadata));
+
+ /* Create Logs directory */
+ wdc_UtilsGetTime(&tarInfo->timeInfo);
+ memset(tarInfo->timeString, 0, sizeof(tarInfo->timeString));
+ wdc_UtilsSnprintf((char*)tarInfo->timeString, MAX_PATH_LEN, "%02u%02u%02u_%02u%02u%02u",
+ tarInfo->timeInfo.year, tarInfo->timeInfo.month, tarInfo->timeInfo.dayOfMonth,
+ tarInfo->timeInfo.hour, tarInfo->timeInfo.minute, tarInfo->timeInfo.second);
+
+ wdc_UtilsSnprintf((char*)tarInfo->bufferFolderName, MAX_PATH_LEN, "%s",
+ (char*)outputName);
+
+ retPtr = getcwd((char*)tarInfo->currDir, MAX_PATH_LEN);
+ if (retPtr != NULL)
+ wdc_UtilsSnprintf((char*)tarInfo->bufferFolderPath, MAX_PATH_LEN, "%s%s%s",
+ (char *)tarInfo->currDir, WDC_DE_PATH_SEPARATOR, (char *)tarInfo->bufferFolderName);
+ else {
+ fprintf(stderr, "ERROR : WDC : get current working directory failed\n");
+ goto free_buf;
+ }
+
+ ret = wdc_UtilsCreateDir((char*)tarInfo->bufferFolderPath);
+ if (ret)
+ {
+ fprintf(stderr, "ERROR : WDC : create directory failed, ret = %d, dir = %s\n", ret, tarInfo->bufferFolderPath);
+ goto free_buf;
+ } else {
+ fprintf(stderr, "Stored log files in directory: %s\n", tarInfo->bufferFolderPath);
+ }
+
+ ret = wdc_do_get_sn730_log_len(fd, &full_log_len, SN730_GET_FULL_LOG_LENGTH);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+ ret = wdc_do_get_sn730_log_len(fd, &key_log_len, SN730_GET_KEY_LOG_LENGTH);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+ ret = wdc_do_get_sn730_log_len(fd, &core_dump_log_len, SN730_GET_COREDUMP_LOG_LENGTH);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+ ret = wdc_do_get_sn730_log_len(fd, &extended_log_len, SN730_GET_EXTENDED_LOG_LENGTH);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+
+ full_log_buf = (uint8_t*) calloc(full_log_len, sizeof (uint8_t));
+ key_log_buf = (uint8_t*) calloc(key_log_len, sizeof (uint8_t));
+ core_dump_log_buf = (uint8_t*) calloc(core_dump_log_len, sizeof (uint8_t));
+ extended_log_buf = (uint8_t*) calloc(extended_log_len, sizeof (uint8_t));
+
+ if (!full_log_buf || !key_log_buf || !core_dump_log_buf || !extended_log_buf) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ ret = -1;
+ goto free_buf;
+ }
+
+ /* Get the full log */
+ ret = get_sn730_log_chunks(fd, full_log_buf, full_log_len, SN730_GET_FULL_LOG_SUBOPCODE);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+
+ /* Get the key log */
+ ret = get_sn730_log_chunks(fd, key_log_buf, key_log_len, SN730_GET_KEY_LOG_SUBOPCODE);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+
+ /* Get the core dump log */
+ ret = get_sn730_log_chunks(fd, core_dump_log_buf, core_dump_log_len, SN730_GET_CORE_LOG_SUBOPCODE);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+
+ /* Get the extended log */
+ ret = get_sn730_log_chunks(fd, extended_log_buf, extended_log_len, SN730_GET_EXTEND_LOG_SUBOPCODE);
+ if (ret) {
+ nvme_show_status(ret);
+ goto free_buf;
+ }
+
+ /* Write log files */
+ wdc_UtilsSnprintf(tarInfo->fileName, MAX_PATH_LEN, "%s%s%s_%s.bin", (char*)tarInfo->bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "full_log", (char*)tarInfo->timeString);
+ wdc_WriteToFile(tarInfo->fileName, (char*)full_log_buf, full_log_len);
+
+ wdc_UtilsSnprintf(tarInfo->fileName, MAX_PATH_LEN, "%s%s%s_%s.bin", (char*)tarInfo->bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "key_log", (char*)tarInfo->timeString);
+ wdc_WriteToFile(tarInfo->fileName, (char*)key_log_buf, key_log_len);
+
+ wdc_UtilsSnprintf(tarInfo->fileName, MAX_PATH_LEN, "%s%s%s_%s.bin", (char*)tarInfo->bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "core_dump_log", (char*)tarInfo->timeString);
+ wdc_WriteToFile(tarInfo->fileName, (char*)core_dump_log_buf, core_dump_log_len);
+
+ wdc_UtilsSnprintf(tarInfo->fileName, MAX_PATH_LEN, "%s%s%s_%s.bin", (char*)tarInfo->bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "extended_log", (char*)tarInfo->timeString);
+ wdc_WriteToFile(tarInfo->fileName, (char*)extended_log_buf, extended_log_len);
+
+ /* Tar the log directory */
+ wdc_UtilsSnprintf(tarInfo->tarFileName, sizeof(tarInfo->tarFileName), "%s%s", (char*)tarInfo->bufferFolderPath, WDC_DE_TAR_FILE_EXTN);
+ wdc_UtilsSnprintf(tarInfo->tarFiles, sizeof(tarInfo->tarFiles), "%s%s%s", (char*)tarInfo->bufferFolderName, WDC_DE_PATH_SEPARATOR, WDC_DE_TAR_FILES);
+ wdc_UtilsSnprintf(tarInfo->tarCmd, sizeof(tarInfo->tarCmd), "%s %s %s", WDC_DE_TAR_CMD, (char*)tarInfo->tarFileName, (char*)tarInfo->tarFiles);
+
+ ret = system(tarInfo->tarCmd);
+
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Tar of log data failed, ret = %d\n", ret);
+
+free_buf:
+ free(tarInfo);
+ free(full_log_buf);
+ free(core_dump_log_buf);
+ free(key_log_buf);
+ free(extended_log_buf);
+ return ret;
+}
+
+static int wdc_vs_internal_fw_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ 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, 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 SN530, SN640, SN730, SN740, SN810, SN840 and ZN350 devices.";
+ char *verbose = "Display more debug messages.";
+ char f[PATH_MAX] = {0};
+ char fileSuffix[PATH_MAX] = {0};
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ __u32 xfer_size = 0;
+ int telemetry_type = 0, telemetry_data_area = 0;
+ UtilsTimeInfo timeInfo;
+ __u8 timeStamp[MAX_PATH_LEN];
+ __u64 capabilities = 0;
+ int ret = -1;
+
+ struct config {
+ char *file;
+ __u32 xfer_size;
+ int data_area;
+ __u64 file_size;
+ __u64 offset;
+ char *type;
+ bool verbose;
+ };
+
+ struct config cfg = {
+ .file = NULL,
+ .xfer_size = 0x10000,
+ .data_area = 0,
+ .file_size = 0,
+ .offset = 0,
+ .type = NULL,
+ .verbose = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FILE("output-file", 'o', &cfg.file, file),
+ OPT_UINT("transfer-size", 's', &cfg.xfer_size, size),
+ OPT_UINT("data-area", 'd', &cfg.data_area, data_area),
+ OPT_LONG("file-size", 'f', &cfg.file_size, file_size),
+ OPT_LONG("offset", 'e', &cfg.offset, offset),
+ OPT_FILE("type", 't', &cfg.type, type),
+ OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ if (!wdc_check_device(r, dev))
+ goto out;
+
+ if (cfg.xfer_size != 0)
+ xfer_size = cfg.xfer_size;
+ else {
+ fprintf(stderr, "ERROR : WDC : Invalid length\n");
+ goto out;
+ }
+
+ if (cfg.file != NULL) {
+ int verify_file;
+
+ /* verify the passed in file name and path is valid before getting the dump data */
+ verify_file = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (verify_file < 0) {
+ fprintf(stderr, "ERROR : WDC: open : %s\n", strerror(errno));
+ goto out;
+ }
+ close(verify_file);
+ strncpy(f, cfg.file, PATH_MAX - 1);
+ } else {
+ wdc_UtilsGetTime(&timeInfo);
+ memset(timeStamp, 0, sizeof(timeStamp));
+ wdc_UtilsSnprintf((char*)timeStamp, MAX_PATH_LEN, "%02u%02u%02u_%02u%02u%02u",
+ timeInfo.year, timeInfo.month, timeInfo.dayOfMonth,
+ timeInfo.hour, timeInfo.minute, timeInfo.second);
+ snprintf(fileSuffix, PATH_MAX, "_internal_fw_log_%s", (char*)timeStamp);
+
+ ret = wdc_get_serial_name(dev, f, PATH_MAX, fileSuffix);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC: failed to generate file name\n");
+ goto out;
+ }
+ }
+
+ if (cfg.file == NULL) {
+ if (strlen(f) > PATH_MAX - 5) {
+ fprintf(stderr, "ERROR : WDC: file name overflow\n");
+ ret = -1;
+ goto out;
+ }
+ strcat(f, ".bin");
+ }
+ fprintf(stderr, "%s: filename = %s\n", __func__, f);
+
+ if (cfg.data_area) {
+ if (cfg.data_area > 5 || cfg.data_area < 1) {
+ fprintf(stderr, "ERROR : WDC: Data area must be 1-5\n");
+ ret = -1;
+ goto out;
+ }
+ }
+
+ if ((cfg.type == NULL) ||
+ (!strcmp(cfg.type, "NONE")) ||
+ (!strcmp(cfg.type, "none"))) {
+ telemetry_type = WDC_TELEMETRY_TYPE_NONE;
+ data_area = 0;
+ } else if ((!strcmp(cfg.type, "HOST")) ||
+ (!strcmp(cfg.type, "host"))) {
+ telemetry_type = WDC_TELEMETRY_TYPE_HOST;
+ telemetry_data_area = cfg.data_area;
+ } else if ((!strcmp(cfg.type, "CONTROLLER")) ||
+ (!strcmp(cfg.type, "controller"))) {
+ telemetry_type = WDC_TELEMETRY_TYPE_CONTROLLER;
+ telemetry_data_area = cfg.data_area;
+ } else {
+ fprintf(stderr, "ERROR : WDC: Invalid type - Must be NONE, HOST or CONTROLLER\n");
+ ret = -1;
+ goto out;
+ }
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) {
+ if (telemetry_data_area == 0)
+ telemetry_data_area = 3; /* Set the default DA to 3 if not specified */
+
+ ret = wdc_do_cap_diag(r, dev, f, xfer_size,
+ telemetry_type, telemetry_data_area);
+ goto out;
+ }
+ if ((capabilities & WDC_DRIVE_CAP_DUI) == WDC_DRIVE_CAP_DUI) {
+ if ((telemetry_type == WDC_TELEMETRY_TYPE_HOST) ||
+ (telemetry_type == WDC_TELEMETRY_TYPE_CONTROLLER)) {
+ if (telemetry_data_area == 0)
+ telemetry_data_area = 3; /* Set the default DA to 3 if not specified */
+ /* Get the desired telemetry log page */
+ ret = wdc_do_cap_telemetry_log(dev, f, xfer_size,
+ telemetry_type, telemetry_data_area);
+ goto out;
+ } else {
+ 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;
+ ret = wdc_do_cap_dui(dev_fd(dev), f, xfer_size,
+ cfg.data_area,
+ cfg.verbose, cfg.file_size,
+ cfg.offset);
+ goto out;
+ }
+ }
+ if ((capabilities & WDC_DRIVE_CAP_DUI_DATA) == WDC_DRIVE_CAP_DUI_DATA){
+ if ((telemetry_type == WDC_TELEMETRY_TYPE_HOST) ||
+ (telemetry_type == WDC_TELEMETRY_TYPE_CONTROLLER)) {
+ if (telemetry_data_area == 0)
+ telemetry_data_area = 3; /* Set the default DA to 3 if not specified */
+ /* Get the desired telemetry log page */
+ ret = wdc_do_cap_telemetry_log(dev, f, xfer_size,
+ telemetry_type, telemetry_data_area);
+ goto out;
+ } else {
+ ret = wdc_do_cap_dui(dev_fd(dev), f, xfer_size,
+ WDC_NVME_DUI_MAX_DATA_AREA,
+ cfg.verbose, 0, 0);
+ goto out;
+ }
+ }
+ if ((capabilities & WDC_SN730B_CAP_VUC_LOG) == WDC_SN730B_CAP_VUC_LOG)
+ ret = wdc_do_sn730_get_and_tar(dev_fd(dev), f);
+ else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ }
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_do_crash_dump(struct nvme_dev *dev, char *file, int type)
+{
+ int ret;
+ __u32 crash_dump_length;
+ __u32 opcode;
+ __u32 cdw12;
+ __u32 cdw10_size;
+ __u32 cdw12_size;
+ __u32 cdw12_clear;
+
+ if (type == WDC_NVME_PFAIL_DUMP_TYPE) {
+ /* set parms to get the PFAIL Crash Dump */
+ opcode = WDC_NVME_PF_CRASH_DUMP_OPCODE;
+ cdw10_size = WDC_NVME_PF_CRASH_DUMP_SIZE_NDT;
+ cdw12_size = ((WDC_NVME_PF_CRASH_DUMP_SIZE_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_PF_CRASH_DUMP_SIZE_CMD);
+
+ cdw12 = (WDC_NVME_PF_CRASH_DUMP_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_PF_CRASH_DUMP_CMD;
+
+ cdw12_clear = ((WDC_NVME_CLEAR_PF_CRASH_DUMP_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_CLEAR_CRASH_DUMP_CMD);
+
+ } else {
+ /* set parms to get the Crash Dump */
+ opcode = WDC_NVME_CRASH_DUMP_OPCODE;
+ cdw10_size = WDC_NVME_CRASH_DUMP_SIZE_NDT;
+ cdw12_size = ((WDC_NVME_CRASH_DUMP_SIZE_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_CRASH_DUMP_SIZE_CMD);
+
+ cdw12 = (WDC_NVME_CRASH_DUMP_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_CRASH_DUMP_CMD;
+
+ cdw12_clear = ((WDC_NVME_CLEAR_CRASH_DUMP_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_CLEAR_CRASH_DUMP_CMD);
+ }
+
+ ret = wdc_dump_length(dev_fd(dev),
+ opcode,
+ cdw10_size,
+ cdw12_size,
+ &crash_dump_length);
+
+ if (ret == -1) {
+ if (type == WDC_NVME_PFAIL_DUMP_TYPE)
+ fprintf(stderr, "INFO : WDC: Pfail dump get size failed\n");
+ else
+ fprintf(stderr, "INFO : WDC: Crash dump get size failed\n");
+
+ return -1;
+ }
+
+ if (crash_dump_length == 0) {
+ if (type == WDC_NVME_PFAIL_DUMP_TYPE)
+ fprintf(stderr, "INFO : WDC: Pfail dump is empty\n");
+ else
+ fprintf(stderr, "INFO : WDC: Crash dump is empty\n");
+ } else {
+ ret = wdc_do_dump(dev,
+ opcode,
+ crash_dump_length,
+ cdw12,
+ file,
+ crash_dump_length);
+
+ if (ret == 0)
+ ret = wdc_do_clear_dump(dev, WDC_NVME_CLEAR_DUMP_OPCODE,
+ cdw12_clear);
+ }
+ return ret;
+}
+
+static int wdc_crash_dump(struct nvme_dev *dev, char *file, int type)
+{
+ char f[PATH_MAX] = {0};
+ const char *dump_type;
+ int ret;
+
+ if (file != NULL) {
+ strncpy(f, file, PATH_MAX - 1);
+ }
+
+ if (type == WDC_NVME_PFAIL_DUMP_TYPE)
+ dump_type = "_pfail_dump";
+ else
+ dump_type = "_crash_dump";
+
+ ret = wdc_get_serial_name(dev, f, PATH_MAX, dump_type);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : failed to generate file name\n");
+ else
+ ret = wdc_do_crash_dump(dev, f, type);
+ return ret;
+}
+
+static int wdc_do_drive_log(struct nvme_dev *dev, char *file)
+{
+ int ret;
+ __u8 *drive_log_data;
+ __u32 drive_log_length;
+ struct nvme_passthru_cmd admin_cmd;
+
+ ret = wdc_dump_length(dev_fd(dev), WDC_NVME_DRIVE_LOG_SIZE_OPCODE,
+ WDC_NVME_DRIVE_LOG_SIZE_NDT,
+ (WDC_NVME_DRIVE_LOG_SIZE_SUBCMD <<
+ WDC_NVME_SUBCMD_SHIFT | WDC_NVME_DRIVE_LOG_SIZE_CMD),
+ &drive_log_length);
+ if (ret == -1) {
+ return -1;
+ }
+
+ drive_log_data = (__u8 *) malloc(sizeof (__u8) * drive_log_length);
+ if (drive_log_data == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(drive_log_data, 0, sizeof (__u8) * drive_log_length);
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = WDC_NVME_DRIVE_LOG_OPCODE;
+ admin_cmd.addr = (__u64)(uintptr_t)drive_log_data;
+ admin_cmd.data_len = drive_log_length;
+ admin_cmd.cdw10 = drive_log_length;
+ admin_cmd.cdw12 = ((WDC_NVME_DRIVE_LOG_SUBCMD <<
+ WDC_NVME_SUBCMD_SHIFT) | WDC_NVME_DRIVE_LOG_SIZE_CMD);
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+ nvme_show_status(ret);
+ if (ret == 0) {
+ ret = wdc_create_log_file(file, drive_log_data, drive_log_length);
+ }
+ free(drive_log_data);
+ return ret;
+}
+
+static int wdc_drive_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Capture Drive Log.";
+ const char *file = "Output file pathname.";
+ char f[PATH_MAX] = {0};
+ struct nvme_dev *dev;
+ int ret;
+ nvme_root_t r;
+ __u64 capabilities = 0;
+ struct config {
+ char *file;
+ };
+
+ struct config cfg = {
+ .file = NULL
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FILE("output-file", 'o', &cfg.file, file),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ if (!wdc_check_device(r, dev)) {
+ nvme_free_tree(r);
+ dev_close(dev);
+ return -1;
+ }
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_DRIVE_LOG) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ if (cfg.file != NULL) {
+ strncpy(f, cfg.file, PATH_MAX - 1);
+ }
+ ret = wdc_get_serial_name(dev, f, PATH_MAX, "drive_log");
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : failed to generate file name\n");
+ else
+ ret = wdc_do_drive_log(dev, f);
+ }
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_crash_dump(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Get Crash Dump.";
+ const char *file = "Output file pathname.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ struct config {
+ char *file;
+ };
+
+ struct config cfg = {
+ .file = NULL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FILE("output-file", 'o', &cfg.file, file),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ if (!wdc_check_device(r, dev)) {
+ nvme_free_tree(r);
+ dev_close(dev);
+ return -1;
+
+ }
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_CRASH_DUMP) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ ret = wdc_crash_dump(dev, cfg.file, WDC_NVME_CRASH_DUMP_TYPE);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : failed to read crash dump\n");
+ }
+ }
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_pfail_dump(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Get Pfail Crash Dump.";
+ char *file = "Output file pathname.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ struct config {
+ char *file;
+ };
+ nvme_root_t r;
+ int ret;
+
+ struct config cfg = {
+ .file = NULL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FILE("output-file", 'o', &cfg.file, file),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ if (!wdc_check_device(r, dev)) {
+ nvme_free_tree(r);
+ dev_close(dev);
+ return -1;
+ }
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_PFAIL_DUMP) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ ret = wdc_crash_dump(dev, cfg.file, WDC_NVME_PFAIL_DUMP_TYPE);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : failed to read pfail crash dump\n");
+ }
+ }
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static void wdc_do_id_ctrl(__u8 *vs, struct json_object *root)
+{
+ char vsn[24] = {0};
+ int base = 3072;
+ int vsn_start = 3081;
+
+ memcpy(vsn, &vs[vsn_start - base], sizeof(vsn));
+ if (root)
+ json_object_add_value_string(root, "wdc vsn", strlen(vsn) > 1 ? vsn : "NULL");
+ else
+ printf("wdc vsn : %s\n", strlen(vsn) > 1 ? vsn : "NULL");
+}
+
+static int wdc_id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ return __id_ctrl(argc, argv, cmd, plugin, wdc_do_id_ctrl);
+}
+
+static const char* wdc_purge_mon_status_to_string(__u32 status)
+{
+ const char *str;
+
+ switch (status) {
+ case WDC_NVME_PURGE_STATE_IDLE:
+ str = "Purge State Idle.";
+ break;
+ case WDC_NVME_PURGE_STATE_DONE:
+ str = "Purge State Done.";
+ break;
+ case WDC_NVME_PURGE_STATE_BUSY:
+ str = "Purge State Busy.";
+ break;
+ case WDC_NVME_PURGE_STATE_REQ_PWR_CYC:
+ str = "Purge Operation resulted in an error that requires "
+ "power cycle.";
+ break;
+ case WDC_NVME_PURGE_STATE_PWR_CYC_PURGE:
+ str = "The previous purge operation was interrupted by a power "
+ "cycle\nor reset interruption. Other commands may be "
+ "rejected until\nPurge Execute is issued and "
+ "completed.";
+ break;
+ default:
+ str = "Unknown.";
+ }
+ return str;
+}
+
+static int wdc_purge(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Send a Purge command.";
+ struct nvme_passthru_cmd admin_cmd;
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ char *err_str;
+ nvme_root_t r;
+ int ret;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ if (!wdc_check_device(r, dev)) {
+ nvme_free_tree(r);
+ dev_close(dev);
+ return -1;
+ }
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if((capabilities & WDC_DRIVE_CAP_PURGE) == 0) {
+ ret = -1;
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ } else {
+ err_str = "";
+ memset(&admin_cmd, 0, sizeof (admin_cmd));
+ admin_cmd.opcode = WDC_NVME_PURGE_CMD_OPCODE;
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd,
+ NULL);
+ if (ret > 0) {
+ switch (ret) {
+ case WDC_NVME_PURGE_CMD_SEQ_ERR:
+ err_str = "ERROR : WDC : Cannot execute purge, "
+ "Purge operation is in progress.\n";
+ break;
+ case WDC_NVME_PURGE_INT_DEV_ERR:
+ err_str = "ERROR : WDC : Internal Device Error.\n";
+ break;
+ default:
+ err_str = "ERROR : WDC\n";
+ }
+ }
+
+ fprintf(stderr, "%s", err_str);
+ nvme_show_status(ret);
+ }
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_purge_monitor(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Send a Purge Monitor command.";
+ __u8 output[WDC_NVME_PURGE_MONITOR_DATA_LEN];
+ double progress_percent;
+ struct nvme_passthru_cmd admin_cmd;
+ struct wdc_nvme_purge_monitor_data *mon;
+ struct nvme_dev *dev;
+ __u64 capabilities;
+ nvme_root_t r;
+ int ret;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ if (!wdc_check_device(r, dev)) {
+ nvme_free_tree(r);
+ dev_close(dev);
+ return -1;
+ }
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if((capabilities & WDC_DRIVE_CAP_PURGE) == 0) {
+ ret = -1;
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ } else {
+ memset(output, 0, sizeof (output));
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = WDC_NVME_PURGE_MONITOR_OPCODE;
+ admin_cmd.addr = (__u64)(uintptr_t)output;
+ admin_cmd.data_len = WDC_NVME_PURGE_MONITOR_DATA_LEN;
+ admin_cmd.cdw10 = WDC_NVME_PURGE_MONITOR_CMD_CDW10;
+ admin_cmd.timeout_ms = WDC_NVME_PURGE_MONITOR_TIMEOUT;
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd,
+ NULL);
+ if (ret == 0) {
+ mon = (struct wdc_nvme_purge_monitor_data *) output;
+ printf("Purge state = 0x%0x\n", admin_cmd.result);
+ printf("%s\n", wdc_purge_mon_status_to_string(admin_cmd.result));
+ if (admin_cmd.result == WDC_NVME_PURGE_STATE_BUSY) {
+ progress_percent =
+ ((double)le32_to_cpu(mon->entire_progress_current) * 100) /
+ le32_to_cpu(mon->entire_progress_total);
+ printf("Purge Progress = %f%%\n", progress_percent);
+ }
+ }
+
+ nvme_show_status(ret);
+ }
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static void wdc_print_log_normal(struct wdc_ssd_perf_stats *perf)
+{
+ printf(" C1 Log Page Performance Statistics :- \n");
+ printf(" Host Read Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->hr_cmds));
+ printf(" Host Read Blocks %20"PRIu64"\n",
+ le64_to_cpu(perf->hr_blks));
+ printf(" Average Read Size %20lf\n",
+ safe_div_fp((le64_to_cpu(perf->hr_blks)), (le64_to_cpu(perf->hr_cmds))));
+ printf(" Host Read Cache Hit Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->hr_ch_cmds));
+ printf(" Host Read Cache Hit_Percentage %20"PRIu64"%%\n",
+ (uint64_t) calc_percent(le64_to_cpu(perf->hr_ch_cmds), le64_to_cpu(perf->hr_cmds)));
+ printf(" Host Read Cache Hit Blocks %20"PRIu64"\n",
+ le64_to_cpu(perf->hr_ch_blks));
+ printf(" Average Read Cache Hit Size %20f\n",
+ safe_div_fp((le64_to_cpu(perf->hr_ch_blks)), (le64_to_cpu(perf->hr_ch_cmds))));
+ printf(" Host Read Commands Stalled %20"PRIu64"\n",
+ le64_to_cpu(perf->hr_st_cmds));
+ printf(" Host Read Commands Stalled Percentage %20"PRIu64"%%\n",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hr_st_cmds)), le64_to_cpu(perf->hr_cmds)));
+ printf(" Host Write Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->hw_cmds));
+ printf(" Host Write Blocks %20"PRIu64"\n",
+ le64_to_cpu(perf->hw_blks));
+ printf(" Average Write Size %20f\n",
+ safe_div_fp((le64_to_cpu(perf->hw_blks)), (le64_to_cpu(perf->hw_cmds))));
+ printf(" Host Write Odd Start Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->hw_os_cmds));
+ printf(" Host Write Odd Start Commands Percentage %20"PRIu64"%%\n",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hw_os_cmds)), (le64_to_cpu(perf->hw_cmds))));
+ printf(" Host Write Odd End Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->hw_oe_cmds));
+ printf(" Host Write Odd End Commands Percentage %20"PRIu64"%%\n",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hw_oe_cmds)), (le64_to_cpu((perf->hw_cmds)))));
+ printf(" Host Write Commands Stalled %20"PRIu64"\n",
+ le64_to_cpu(perf->hw_st_cmds));
+ printf(" Host Write Commands Stalled Percentage %20"PRIu64"%%\n",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hw_st_cmds)), (le64_to_cpu(perf->hw_cmds))));
+ printf(" NAND Read Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->nr_cmds));
+ printf(" NAND Read Blocks Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->nr_blks));
+ printf(" Average NAND Read Size %20f\n",
+ safe_div_fp((le64_to_cpu(perf->nr_blks)), (le64_to_cpu((perf->nr_cmds)))));
+ printf(" Nand Write Commands %20"PRIu64"\n",
+ le64_to_cpu(perf->nw_cmds));
+ printf(" NAND Write Blocks %20"PRIu64"\n",
+ le64_to_cpu(perf->nw_blks));
+ printf(" Average NAND Write Size %20f\n",
+ safe_div_fp((le64_to_cpu(perf->nw_blks)), (le64_to_cpu(perf->nw_cmds))));
+ printf(" NAND Read Before Write %20"PRIu64"\n",
+ le64_to_cpu(perf->nrbw));
+}
+
+static void wdc_print_log_json(struct wdc_ssd_perf_stats *perf)
+{
+ struct json_object *root;
+
+ root = json_create_object();
+ json_object_add_value_int(root, "Host Read Commands", le64_to_cpu(perf->hr_cmds));
+ json_object_add_value_int(root, "Host Read Blocks", le64_to_cpu(perf->hr_blks));
+ json_object_add_value_int(root, "Average Read Size",
+ safe_div_fp((le64_to_cpu(perf->hr_blks)), (le64_to_cpu(perf->hr_cmds))));
+ json_object_add_value_int(root, "Host Read Cache Hit Commands",
+ le64_to_cpu(perf->hr_ch_cmds));
+ json_object_add_value_int(root, "Host Read Cache Hit Percentage",
+ (uint64_t) calc_percent(le64_to_cpu(perf->hr_ch_cmds), le64_to_cpu(perf->hr_cmds)));
+ json_object_add_value_int(root, "Host Read Cache Hit Blocks",
+ le64_to_cpu(perf->hr_ch_blks));
+ json_object_add_value_int(root, "Average Read Cache Hit Size",
+ safe_div_fp((le64_to_cpu(perf->hr_ch_blks)), (le64_to_cpu(perf->hr_ch_cmds))));
+ json_object_add_value_int(root, "Host Read Commands Stalled",
+ le64_to_cpu(perf->hr_st_cmds));
+ json_object_add_value_int(root, "Host Read Commands Stalled Percentage",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hr_st_cmds)), le64_to_cpu(perf->hr_cmds)));
+ json_object_add_value_int(root, "Host Write Commands",
+ le64_to_cpu(perf->hw_cmds));
+ json_object_add_value_int(root, "Host Write Blocks",
+ le64_to_cpu(perf->hw_blks));
+ json_object_add_value_int(root, "Average Write Size",
+ safe_div_fp((le64_to_cpu(perf->hw_blks)), (le64_to_cpu(perf->hw_cmds))));
+ json_object_add_value_int(root, "Host Write Odd Start Commands",
+ le64_to_cpu(perf->hw_os_cmds));
+ json_object_add_value_int(root, "Host Write Odd Start Commands Percentage",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hw_os_cmds)), (le64_to_cpu(perf->hw_cmds))));
+ json_object_add_value_int(root, "Host Write Odd End Commands",
+ le64_to_cpu(perf->hw_oe_cmds));
+ json_object_add_value_int(root, "Host Write Odd End Commands Percentage",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hw_oe_cmds)), (le64_to_cpu((perf->hw_cmds)))));
+ json_object_add_value_int(root, "Host Write Commands Stalled",
+ le64_to_cpu(perf->hw_st_cmds));
+ json_object_add_value_int(root, "Host Write Commands Stalled Percentage",
+ (uint64_t)calc_percent((le64_to_cpu(perf->hw_st_cmds)), (le64_to_cpu(perf->hw_cmds))));
+ json_object_add_value_int(root, "NAND Read Commands",
+ le64_to_cpu(perf->nr_cmds));
+ json_object_add_value_int(root, "NAND Read Blocks Commands",
+ le64_to_cpu(perf->nr_blks));
+ json_object_add_value_int(root, "Average NAND Read Size",
+ safe_div_fp((le64_to_cpu(perf->nr_blks)), (le64_to_cpu((perf->nr_cmds)))));
+ json_object_add_value_int(root, "Nand Write Commands",
+ le64_to_cpu(perf->nw_cmds));
+ json_object_add_value_int(root, "NAND Write Blocks",
+ le64_to_cpu(perf->nw_blks));
+ json_object_add_value_int(root, "Average NAND Write Size",
+ safe_div_fp((le64_to_cpu(perf->nw_blks)), (le64_to_cpu(perf->nw_cmds))));
+ json_object_add_value_int(root, "NAND Read Before Written",
+ le64_to_cpu(perf->nrbw));
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static int wdc_print_log(struct wdc_ssd_perf_stats *perf, int fmt)
+{
+ if (!perf) {
+ fprintf(stderr, "ERROR : WDC : Invalid buffer to read perf stats\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_log_normal(perf);
+ break;
+ case JSON:
+ wdc_print_log_json(perf);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_convert_ts(time_t time, char *ts_buf)
+{
+ struct tm gmTimeInfo;
+ time_t time_Human, time_ms;
+ char buf[80];
+
+ time_Human = time/1000;
+ time_ms = time % 1000;
+
+ gmtime_r((const time_t *)&time_Human, &gmTimeInfo);
+
+ strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &gmTimeInfo);
+ sprintf(ts_buf, "%s.%03ld GMT", buf, time_ms);
+
+ return 0;
+}
+
+static int wdc_print_latency_monitor_log_normal(struct nvme_dev *dev,
+ struct wdc_ssd_latency_monitor_log *log_data)
+{
+ printf("Latency Monitor/C3 Log Page Data \n");
+ printf(" Controller : %s\n", dev->name);
+ int err = -1, i, j;
+ struct nvme_id_ctrl ctrl;
+ char ts_buf[128];
+
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (!err)
+ printf(" Serial Number: %-.*s\n", (int)sizeof(ctrl.sn), ctrl.sn);
+ else {
+ fprintf(stderr, "ERROR : WDC : latency monitor read id ctrl failure, err = %d\n", err);
+ return err;
+ }
+
+ printf(" Feature Status 0x%x \n", log_data->feature_status);
+ printf(" Active Bucket Timer %d min \n", 5*le16_to_cpu(log_data->active_bucket_timer));
+ printf(" Active Bucket Timer Threshold %d min \n", 5*le16_to_cpu(log_data->active_bucket_timer_threshold));
+ printf(" Active Threshold A %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_a+1)));
+ printf(" Active Threshold B %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_b+1)));
+ printf(" Active Threshold C %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_c+1)));
+ printf(" Active Threshold D %d ms \n", 5*(le16_to_cpu(log_data->active_threshold_d+1)));
+ printf(" Active Latency Config 0x%x \n", le16_to_cpu(log_data->active_latency_config));
+ printf(" Active Latency Minimum Window %d ms \n", 100*log_data->active_latency_min_window);
+ printf(" Active Latency Stamp Units %d \n", le16_to_cpu(log_data->active_latency_stamp_units));
+ printf(" Static Latency Stamp Units %d \n", le16_to_cpu(log_data->static_latency_stamp_units));
+ printf(" Debug Log Trigger Enable %d \n", le16_to_cpu(log_data->debug_log_trigger_enable));
+
+ printf(" Read Write Deallocate/Trim \n");
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Bucket Counter: Bucket %d %27d %27d %27d \n",
+ i, le32_to_cpu(log_data->active_bucket_counter[i][READ]), le32_to_cpu(log_data->active_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->active_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
+ i, le16_to_cpu(log_data->active_measured_latency[i][READ]), le16_to_cpu(log_data->active_measured_latency[i][WRITE]),
+ le16_to_cpu(log_data->active_measured_latency[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Active Latency Time Stamp: Bucket %d ", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->active_latency_timestamp[i][j]) == -1)
+ printf(" N/A ");
+ else {
+ wdc_convert_ts(le64_to_cpu(log_data->active_latency_timestamp[i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Bucket Counter: Bucket %d %27d %27d %27d \n",
+ i, le32_to_cpu(log_data->static_bucket_counter[i][READ]), le32_to_cpu(log_data->static_bucket_counter[i][WRITE]),
+ le32_to_cpu(log_data->static_bucket_counter[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Measured Latency: Bucket %d %27d ms %27d ms %27d ms \n",
+ i, le16_to_cpu(log_data->static_measured_latency[i][READ]), le16_to_cpu(log_data->static_measured_latency[i][WRITE]),
+ le16_to_cpu(log_data->static_measured_latency[i][TRIM]));
+ }
+
+ for (i = 0; i <= 3; i++) {
+ printf(" Static Latency Time Stamp: Bucket %d ", i);
+ for (j = 0; j <= 2; j++) {
+ if (le64_to_cpu(log_data->static_latency_timestamp[i][j]) == -1)
+ printf(" N/A ");
+ else {
+ wdc_convert_ts(le64_to_cpu(log_data->static_latency_timestamp[i][j]), ts_buf);
+ printf("%s ", ts_buf);
+ }
+ }
+ printf("\n");
+ }
+
+ return 0;
+}
+
+static void wdc_print_latency_monitor_log_json(struct wdc_ssd_latency_monitor_log *log_data)
+{
+ int i, j;
+ char buf[128];
+ char *operation[3] = {"Read", "Write", "Trim"};
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Feature Status", log_data->feature_status);
+ json_object_add_value_int(root, "Active Bucket Timer", 5*le16_to_cpu(log_data->active_bucket_timer));
+ json_object_add_value_int(root, "Active Bucket Timer Threshold", 5*le16_to_cpu(log_data->active_bucket_timer_threshold));
+ json_object_add_value_int(root, "Active Threshold A", 5*le16_to_cpu(log_data->active_threshold_a+1));
+ json_object_add_value_int(root, "Active Threshold B", 5*le16_to_cpu(log_data->active_threshold_b+1));
+ json_object_add_value_int(root, "Active Threshold C", 5*le16_to_cpu(log_data->active_threshold_c+1));
+ json_object_add_value_int(root, "Active Threshold D", 5*le16_to_cpu(log_data->active_threshold_d+1));
+ json_object_add_value_int(root, "Active Latency Config", le16_to_cpu(log_data->active_latency_config));
+ json_object_add_value_int(root, "Active Lantency Minimum Window", 100*log_data->active_latency_min_window);
+ json_object_add_value_int(root, "Active Latency Stamp Units", le16_to_cpu(log_data->active_latency_stamp_units));
+ json_object_add_value_int(root, "Static Latency Stamp Units", le16_to_cpu(log_data->static_latency_stamp_units));
+ json_object_add_value_int(root, "Debug Log Trigger Enable", le16_to_cpu(log_data->debug_log_trigger_enable));
+
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Active Bucket Counter: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le32_to_cpu(log_data->active_bucket_counter[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Active Measured Latency: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le16_to_cpu(log_data->active_measured_latency[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Active Latency Time Stamp: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le64_to_cpu(log_data->active_latency_timestamp[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Static Bucket Counter: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le32_to_cpu(log_data->static_bucket_counter[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Static Measured Latency: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le16_to_cpu(log_data->static_measured_latency[i][j]));
+ }
+ }
+ for (i = 0; i <= 3; i++) {
+ for (j = 0; j <= 2; j++) {
+ sprintf(buf, "Static Latency Time Stamp: Bucket %d %s", i, operation[j]);
+ json_object_add_value_int(root, buf, le64_to_cpu(log_data->static_latency_timestamp[i][j]));
+ }
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static void wdc_print_error_rec_log_normal(struct wdc_ocp_c1_error_recovery_log *log_data)
+{
+ int j;
+ printf("Error Recovery/C1 Log Page Data \n");
+
+ printf(" Panic Reset Wait Time : 0x%x \n", le16_to_cpu(log_data->panic_reset_wait_time));
+ printf(" Panic Reset Action : 0x%x \n", log_data->panic_reset_action);
+ printf(" Device Recovery Action 1 : 0x%x \n", log_data->dev_recovery_action1);
+ printf(" Panic ID : 0x%" PRIu64 "\n", le64_to_cpu(log_data->panic_id));
+ printf(" Device Capabilities : 0x%x \n", le32_to_cpu(log_data->dev_capabilities));
+ printf(" Vendor Specific Recovery Opcode : 0x%x \n", log_data->vs_recovery_opc);
+ printf(" Vendor Specific Command CDW12 : 0x%x \n", le32_to_cpu(log_data->vs_cmd_cdw12));
+ printf(" Vendor Specific Command CDW13 : 0x%x \n", le32_to_cpu(log_data->vs_cmd_cdw13));
+ if (le16_to_cpu(log_data->log_page_version) == WDC_ERROR_REC_LOG_VERSION2) {
+ printf(" Vendor Specific Command Timeout : 0x%x \n", log_data->vs_cmd_to);
+ printf(" Device Recovery Action 2 : 0x%x \n", log_data->dev_recovery_action2);
+ printf(" Device Recovery Action 2 Timeout : 0x%x \n", log_data->dev_recovery_action2_to);
+ }
+ printf(" Log Page Version : 0x%x \n", le16_to_cpu(log_data->log_page_version));
+ printf(" Log page GUID : 0x");
+ for (j = 0; j < WDC_OCP_C1_GUID_LENGTH; j++) {
+ printf("%x", log_data->log_page_guid[j]);
+ }
+ printf("\n");
+}
+
+static void wdc_print_error_rec_log_json(struct wdc_ocp_c1_error_recovery_log *log_data)
+{
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Panic Reset Wait Time", le16_to_cpu(log_data->panic_reset_wait_time));
+ json_object_add_value_int(root, "Panic Reset Action", log_data->panic_reset_wait_time);
+ json_object_add_value_int(root, "Device Recovery Action 1", log_data->dev_recovery_action1);
+ json_object_add_value_int(root, "Panic ID", le64_to_cpu(log_data->panic_id));
+ json_object_add_value_int(root, "Device Capabilities", le32_to_cpu(log_data->dev_capabilities));
+ json_object_add_value_int(root, "Vendor Specific Recovery Opcode", log_data->vs_recovery_opc);
+ json_object_add_value_int(root, "Vendor Specific Command CDW12", le32_to_cpu(log_data->vs_cmd_cdw12));
+ json_object_add_value_int(root, "Vendor Specific Command CDW13", le32_to_cpu(log_data->vs_cmd_cdw13));
+ if (le16_to_cpu(log_data->log_page_version) == WDC_ERROR_REC_LOG_VERSION2) {
+ json_object_add_value_int(root, "Vendor Specific Command Timeout", log_data->vs_cmd_to);
+ json_object_add_value_int(root, "Device Recovery Action 2", log_data->dev_recovery_action2);
+ json_object_add_value_int(root, "Device Recovery Action 2 Timeout", log_data->dev_recovery_action2_to);
+ }
+ json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version));
+
+ char guid[40];
+ memset((void*)guid, 0, 40);
+ sprintf((char*)guid, "0x%"PRIx64"%"PRIx64"",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0]));
+ json_object_add_value_string(root, "Log page GUID", guid);
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static void wdc_print_dev_cap_log_normal(struct wdc_ocp_C4_dev_cap_log *log_data)
+{
+ int j;
+ printf("Device Capabilities/C4 Log Page Data \n");
+
+ printf(" Number PCIE Ports : 0x%x \n", le16_to_cpu(log_data->num_pcie_ports));
+ printf(" Number OOB Management Interfaces : 0x%x \n", le16_to_cpu(log_data->oob_mgmt_support));
+ printf(" Write Zeros Command Support : 0x%x \n", le16_to_cpu(log_data->wrt_zeros_support));
+ printf(" Sanitize Command Support : 0x%x \n", le16_to_cpu(log_data->sanitize_support));
+ printf(" DSM Command Support : 0x%x \n", le16_to_cpu(log_data->dsm_support));
+ printf(" Write Uncorr Command Support : 0x%x \n", le16_to_cpu(log_data->wrt_uncor_support));
+ printf(" Fused Command Support : 0x%x \n", le16_to_cpu(log_data->fused_support));
+ printf(" Minimum DSSD Power State : 0x%x \n", le16_to_cpu(log_data->min_dssd_ps));
+
+ for (j = 0; j < WDC_OCP_C4_NUM_PS_DESCR; j++) {
+ printf(" DSSD Power State %d Desriptor : 0x%x \n", j, log_data->dssd_ps_descr[j]);
+ }
+
+ printf(" Log Page Version : 0x%x \n", le16_to_cpu(log_data->log_page_version));
+ printf(" Log page GUID : 0x");
+ for (j = 0; j < WDC_OCP_C4_GUID_LENGTH; j++) {
+ printf("%x", log_data->log_page_guid[j]);
+ }
+ printf("\n");
+}
+
+static void wdc_print_dev_cap_log_json(struct wdc_ocp_C4_dev_cap_log *log_data)
+{
+ int j;
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Number PCIE Ports", le16_to_cpu(log_data->num_pcie_ports));
+ json_object_add_value_int(root, "Number OOB Management Interfaces", le16_to_cpu(log_data->num_pcie_ports));
+ json_object_add_value_int(root, "Write Zeros Command Support", le16_to_cpu(log_data->num_pcie_ports));
+ json_object_add_value_int(root, "Sanitize Command Support", le16_to_cpu(log_data->num_pcie_ports));
+ json_object_add_value_int(root, "DSM Command Support", le16_to_cpu(log_data->num_pcie_ports));
+ json_object_add_value_int(root, "Write Uncorr Command Support", le16_to_cpu(log_data->num_pcie_ports));
+ json_object_add_value_int(root, "Fused Command Support", le16_to_cpu(log_data->num_pcie_ports));
+ json_object_add_value_int(root, "Minimum DSSD Power State", le16_to_cpu(log_data->num_pcie_ports));
+
+ char dssd_descr_str[40];
+ memset((void *)dssd_descr_str, 0, 40);
+ for (j = 0; j < WDC_OCP_C4_NUM_PS_DESCR; j++) {
+ sprintf((char *)dssd_descr_str, "DSSD Power State %d Descriptor", j);
+ json_object_add_value_int(root, dssd_descr_str, log_data->dssd_ps_descr[j]);
+ }
+
+ json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version));
+ char guid[40];
+ memset((void*)guid, 0, 40);
+ sprintf((char*)guid, "0x%"PRIx64"%"PRIx64"",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0]));
+ json_object_add_value_string(root, "Log page GUID", guid);
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static void wdc_print_unsupported_reqs_log_normal(struct wdc_ocp_C5_unsupported_reqs *log_data)
+{
+ int j;
+ printf("Unsupported Requirements/C5 Log Page Data \n");
+
+ printf(" Number Unsupported Req IDs : 0x%x \n", le16_to_cpu(log_data->unsupported_count));
+
+ for (j = 0; j < le16_to_cpu(log_data->unsupported_count); j++) {
+ printf(" Unsupported Requirement List %d : %s \n", j, log_data->unsupported_req_list[j]);
+ }
+
+ printf(" Log Page Version : 0x%x \n", le16_to_cpu(log_data->log_page_version));
+ printf(" Log page GUID : 0x");
+ for (j = 0; j < WDC_OCP_C5_GUID_LENGTH; j++) {
+ printf("%x", log_data->log_page_guid[j]);
+ }
+ printf("\n");
+}
+
+static void wdc_print_unsupported_reqs_log_json(struct wdc_ocp_C5_unsupported_reqs *log_data)
+{
+ int j;
+ struct json_object *root;
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Number Unsupported Req IDs", le16_to_cpu(log_data->unsupported_count));
+
+ char unsup_req_list_str[40];
+ memset((void *)unsup_req_list_str, 0, 40);
+ for (j = 0; j < le16_to_cpu(log_data->unsupported_count); j++) {
+ sprintf((char *)unsup_req_list_str, "Unsupported Requirement List %d", j);
+ json_object_add_value_string(root, unsup_req_list_str, (char *)log_data->unsupported_req_list[j]);
+ }
+
+ json_object_add_value_int(root, "Log Page Version", le16_to_cpu(log_data->log_page_version));
+ char guid[40];
+ memset((void*)guid, 0, 40);
+ sprintf((char*)guid, "0x%"PRIx64"%"PRIx64"",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data->log_page_guid[0]));
+ json_object_add_value_string(root, "Log page GUID", guid);
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static void wdc_print_fb_ca_log_normal(struct wdc_ssd_ca_perf_stats *perf)
+{
+ uint64_t converted = 0;
+
+ printf(" CA Log Page Performance Statistics :- \n");
+ printf(" NAND Bytes Written %20"PRIu64 "%20"PRIu64"\n",
+ le64_to_cpu(perf->nand_bytes_wr_hi), le64_to_cpu(perf->nand_bytes_wr_lo));
+ printf(" NAND Bytes Read %20"PRIu64 "%20"PRIu64"\n",
+ le64_to_cpu(perf->nand_bytes_rd_hi), le64_to_cpu(perf->nand_bytes_rd_lo));
+
+ converted = le64_to_cpu(perf->nand_bad_block);
+ printf(" NAND Bad Block Count (Normalized) %20"PRIu64"\n",
+ converted & 0xFFFF);
+ printf(" NAND Bad Block Count (Raw) %20"PRIu64"\n",
+ converted >> 16);
+
+ printf(" Uncorrectable Read Count %20"PRIu64"\n",
+ le64_to_cpu(perf->uncorr_read_count));
+ printf(" Soft ECC Error Count %20"PRIu64"\n",
+ le64_to_cpu(perf->ecc_error_count));
+ printf(" SSD End to End Detected Correction Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->ssd_detect_count));
+ printf(" SSD End to End Corrected Correction Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->ssd_correct_count));
+ printf(" System Data Percent Used %20"PRIu32"%%\n",
+ perf->data_percent_used);
+ printf(" User Data Erase Counts Max %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->data_erase_max));
+ printf(" User Data Erase Counts Min %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->data_erase_min));
+ printf(" Refresh Count %20"PRIu64"\n",
+ le64_to_cpu(perf->refresh_count));
+
+ converted = le64_to_cpu(perf->program_fail);
+ printf(" Program Fail Count (Normalized) %20"PRIu64"\n",
+ converted & 0xFFFF);
+ printf(" Program Fail Count (Raw) %20"PRIu64"\n",
+ converted >> 16);
+
+ converted = le64_to_cpu(perf->user_erase_fail);
+ printf(" User Data Erase Fail Count (Normalized) %20"PRIu64"\n",
+ converted & 0xFFFF);
+ printf(" User Data Erase Fail Count (Raw) %20"PRIu64"\n",
+ converted >> 16);
+
+ converted = le64_to_cpu(perf->system_erase_fail);
+ printf(" System Area Erase Fail Count (Normalized) %20"PRIu64"\n",
+ converted & 0xFFFF);
+ printf(" System Area Erase Fail Count (Raw) %20"PRIu64"\n",
+ converted >> 16);
+
+ printf(" Thermal Throttling Status %20"PRIu8"\n",
+ perf->thermal_throttle_status);
+ printf(" Thermal Throttling Count %20"PRIu8"\n",
+ perf->thermal_throttle_count);
+ printf(" PCIe Correctable Error Count %20"PRIu64"\n",
+ le64_to_cpu(perf->pcie_corr_error));
+ printf(" Incomplete Shutdown Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->incomplete_shutdown_count));
+ printf(" Percent Free Blocks %20"PRIu32"%%\n",
+ perf->percent_free_blocks);
+}
+
+static void wdc_print_fb_ca_log_json(struct wdc_ssd_ca_perf_stats *perf)
+{
+ struct json_object *root;
+ uint64_t converted = 0;
+
+ root = json_create_object();
+ json_object_add_value_int(root, "NAND Bytes Written Hi", le64_to_cpu(perf->nand_bytes_wr_hi));
+ json_object_add_value_int(root, "NAND Bytes Written Lo", le64_to_cpu(perf->nand_bytes_wr_lo));
+ json_object_add_value_int(root, "NAND Bytes Read Hi", le64_to_cpu(perf->nand_bytes_rd_hi));
+ json_object_add_value_int(root, "NAND Bytes Read Lo", le64_to_cpu(perf->nand_bytes_rd_lo));
+
+ converted = le64_to_cpu(perf->nand_bad_block);
+ json_object_add_value_int(root, "NAND Bad Block Count (Normalized)",
+ converted & 0xFFFF);
+ json_object_add_value_int(root, "NAND Bad Block Count (Raw)",
+ converted >> 16);
+
+ json_object_add_value_int(root, "Uncorrectable Read Count", le64_to_cpu(perf->uncorr_read_count));
+ json_object_add_value_int(root, "Soft ECC Error Count", le64_to_cpu(perf->ecc_error_count));
+ json_object_add_value_int(root, "SSD End to End Detected Correction Count",
+ le32_to_cpu(perf->ssd_detect_count));
+ json_object_add_value_int(root, "SSD End to End Corrected Correction Count",
+ le32_to_cpu(perf->ssd_correct_count));
+ json_object_add_value_int(root, "System Data Percent Used",
+ perf->data_percent_used);
+ json_object_add_value_int(root, "User Data Erase Counts Max",
+ le32_to_cpu(perf->data_erase_max));
+ json_object_add_value_int(root, "User Data Erase Counts Min",
+ le32_to_cpu(perf->data_erase_min));
+ json_object_add_value_int(root, "Refresh Count", le64_to_cpu(perf->refresh_count));
+
+ converted = le64_to_cpu(perf->program_fail);
+ json_object_add_value_int(root, "Program Fail Count (Normalized)",
+ converted & 0xFFFF);
+ json_object_add_value_int(root, "Program Fail Count (Raw)",
+ converted >> 16);
+
+ converted = le64_to_cpu(perf->user_erase_fail);
+ json_object_add_value_int(root, "User Data Erase Fail Count (Normalized)",
+ converted & 0xFFFF);
+ json_object_add_value_int(root, "User Data Erase Fail Count (Raw)",
+ converted >> 16);
+
+ converted = le64_to_cpu(perf->system_erase_fail);
+ json_object_add_value_int(root, "System Area Erase Fail Count (Normalized)",
+ converted & 0xFFFF);
+ json_object_add_value_int(root, "System Area Erase Fail Count (Raw)",
+ converted >> 16);
+
+ json_object_add_value_int(root, "Thermal Throttling Status",
+ perf->thermal_throttle_status);
+ json_object_add_value_int(root, "Thermal Throttling Count",
+ perf->thermal_throttle_count);
+ json_object_add_value_int(root, "PCIe Correctable Error", le64_to_cpu(perf->pcie_corr_error));
+ json_object_add_value_int(root, "Incomplete Shutdown Counte", le32_to_cpu(perf->incomplete_shutdown_count));
+ json_object_add_value_int(root, "Percent Free Blocks", perf->percent_free_blocks);
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static void wdc_print_bd_ca_log_normal(struct nvme_dev *dev, void *data)
+{
+ struct wdc_bd_ca_log_format *bd_data = (struct wdc_bd_ca_log_format *)data;
+ __u64 *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[0];
+ printf("Additional Smart Log for NVME device:%s namespace-id:%x\n",
+ dev->name, 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[0];
+ 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_raw1 = (__u16*)&bd_data->raw_value[1];
+ word_raw2 = (__u16*)&bd_data->raw_value[3];
+ word_raw3 = (__u16*)&bd_data->raw_value[5];
+ 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[0];
+ 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[0];
+ 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[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[0];
+ 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[0];
+ 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[1];
+ dword_raw = (__u32*)&bd_data->raw_value[2];
+ 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[0];
+ 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[0];
+ 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[0];
+ printf("nand_bytes_written : %3"PRIu8"%% sectors: %.f\n",
+ bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0C) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ printf("host_bytes_written : %3"PRIu8"%% sectors: %.f\n",
+ bd_data->normalized_value, safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ } else {
+ goto invalid_id;
+ }
+
+ goto done;
+
+ invalid_id:
+ printf(" Invalid Field ID = %d\n", bd_data->field_id);
+
+ done:
+ return;
+
+}
+
+static void wdc_print_bd_ca_log_json(void *data)
+{
+ struct wdc_bd_ca_log_format *bd_data = (struct wdc_bd_ca_log_format *)data;
+ __u64 *raw;
+ __u16 *word_raw;
+ __u32 *dword_raw;
+ __u8 *byte_raw;
+ struct json_object *root;
+
+ root = json_create_object();
+ if (bd_data->field_id == 0x00) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "program_fail_count normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "program_fail_count raw",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x01) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "erase_fail_count normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "erase_fail_count raw",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x02) {
+ word_raw = (__u16*)&bd_data->raw_value[1];
+ json_object_add_value_int(root, "wear_leveling normalized", bd_data->normalized_value);
+ json_object_add_value_int(root, "wear_leveling min", le16_to_cpu(*word_raw));
+ word_raw = (__u16*)&bd_data->raw_value[3];
+ json_object_add_value_int(root, "wear_leveling max", le16_to_cpu(*word_raw));
+ word_raw = (__u16*)&bd_data->raw_value[5];
+ json_object_add_value_int(root, "wear_leveling avg", le16_to_cpu(*word_raw));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x03) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "end_to_end_error_detection_count normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "end_to_end_error_detection_count raw",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x04) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "crc_error_count normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "crc_error_count raw",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x05) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "timed_workload_media_wear normalized",
+ bd_data->normalized_value);
+ json_object_add_value_double(root, "timed_workload_media_wear raw",
+ 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[0];
+ json_object_add_value_int(root, "timed_workload_host_reads normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "timed_workload_host_reads raw",
+ le64_to_cpu(*raw & 0x00000000000000FF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x07) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "timed_workload_timer normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "timed_workload_timer",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x08) {
+ byte_raw = (__u8*)&bd_data->raw_value[1];
+ json_object_add_value_int(root, "thermal_throttle_status normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "thermal_throttle_status", *byte_raw);
+ dword_raw = (__u32*)&bd_data->raw_value[2];
+ json_object_add_value_int(root, "thermal_throttle_cnt", le32_to_cpu(*dword_raw));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x09) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "retry_buffer_overflow_count normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "retry_buffer_overflow_count raw",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0A) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "pll_lock_loss_count normalized",
+ bd_data->normalized_value);
+ json_object_add_value_int(root, "pll_lock_loss_count raw",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0B) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "nand_bytes_written normalized",
+ bd_data->normalized_value);
+ json_object_add_value_double(root, "nand_bytes_written raw",
+ safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0C) {
+ raw = (__u64*)&bd_data->raw_value[0];
+ json_object_add_value_int(root, "host_bytes_written normalized",
+ bd_data->normalized_value);
+ json_object_add_value_double(root, "host_bytes_written raw",
+ safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ } else {
+ goto invalid_id;
+ }
+
+ goto done;
+
+ invalid_id:
+ printf(" Invalid Field ID = %d\n", bd_data->field_id);
+
+ done:
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+
+ return;
+
+}
+
+static void wdc_print_d0_log_normal(struct wdc_ssd_d0_smart_log *perf)
+{
+ printf(" D0 Smart Log Page Statistics :- \n");
+ printf(" Lifetime Reallocated Erase Block Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_realloc_erase_block_count));
+ printf(" Lifetime Power on Hours %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_power_on_hours));
+ printf(" Lifetime UECC Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_uecc_count));
+ printf(" Lifetime Write Amplification Factor %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_wrt_amp_factor));
+ printf(" Trailing Hour Write Amplification Factor %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->trailing_hr_wrt_amp_factor));
+ printf(" Reserve Erase Block Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->reserve_erase_block_count));
+ printf(" Lifetime Program Fail Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_program_fail_count));
+ printf(" Lifetime Block Erase Fail Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_block_erase_fail_count));
+ printf(" Lifetime Die Failure Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_die_failure_count));
+ printf(" Lifetime Link Rate Downgrade Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_link_rate_downgrade_count));
+ printf(" Lifetime Clean Shutdown Count on Power Loss %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_clean_shutdown_count));
+ printf(" Lifetime Unclean Shutdowns on Power Loss %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_unclean_shutdown_count));
+ printf(" Current Temperature %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->current_temp));
+ printf(" Max Recorded Temperature %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->max_recorded_temp));
+ printf(" Lifetime Retired Block Count %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_retired_block_count));
+ printf(" Lifetime Read Disturb Reallocation Events %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_read_disturb_realloc_events));
+ printf(" Lifetime NAND Writes %20"PRIu64"\n",
+ le64_to_cpu(perf->lifetime_nand_writes));
+ printf(" Capacitor Health %20"PRIu32"%%\n",
+ (uint32_t)le32_to_cpu(perf->capacitor_health));
+ printf(" Lifetime User Writes %20"PRIu64"\n",
+ le64_to_cpu(perf->lifetime_user_writes));
+ printf(" Lifetime User Reads %20"PRIu64"\n",
+ le64_to_cpu(perf->lifetime_user_reads));
+ printf(" Lifetime Thermal Throttle Activations %20"PRIu32"\n",
+ (uint32_t)le32_to_cpu(perf->lifetime_thermal_throttle_act));
+ printf(" Percentage of P/E Cycles Remaining %20"PRIu32"%%\n",
+ (uint32_t)le32_to_cpu(perf->percentage_pe_cycles_remaining));
+}
+
+static void wdc_print_d0_log_json(struct wdc_ssd_d0_smart_log *perf)
+{
+ struct json_object *root;
+
+ root = json_create_object();
+ json_object_add_value_int(root, "Lifetime Reallocated Erase Block Count",
+ le32_to_cpu(perf->lifetime_realloc_erase_block_count));
+ json_object_add_value_int(root, "Lifetime Power on Hours",
+ le32_to_cpu(perf->lifetime_power_on_hours));
+ json_object_add_value_int(root, "Lifetime UECC Count",
+ le32_to_cpu(perf->lifetime_uecc_count));
+ json_object_add_value_int(root, "Lifetime Write Amplification Factor",
+ le32_to_cpu(perf->lifetime_wrt_amp_factor));
+ json_object_add_value_int(root, "Trailing Hour Write Amplification Factor",
+ le32_to_cpu(perf->trailing_hr_wrt_amp_factor));
+ json_object_add_value_int(root, "Reserve Erase Block Count",
+ le32_to_cpu(perf->reserve_erase_block_count));
+ json_object_add_value_int(root, "Lifetime Program Fail Count",
+ le32_to_cpu(perf->lifetime_program_fail_count));
+ json_object_add_value_int(root, "Lifetime Block Erase Fail Count",
+ le32_to_cpu(perf->lifetime_block_erase_fail_count));
+ json_object_add_value_int(root, "Lifetime Die Failure Count",
+ le32_to_cpu(perf->lifetime_die_failure_count));
+ json_object_add_value_int(root, "Lifetime Link Rate Downgrade Count",
+ le32_to_cpu(perf->lifetime_link_rate_downgrade_count));
+ json_object_add_value_int(root, "Lifetime Clean Shutdown Count on Power Loss",
+ le32_to_cpu(perf->lifetime_clean_shutdown_count));
+ json_object_add_value_int(root, "Lifetime Unclean Shutdowns on Power Loss",
+ le32_to_cpu(perf->lifetime_unclean_shutdown_count));
+ json_object_add_value_int(root, "Current Temperature",
+ le32_to_cpu(perf->current_temp));
+ json_object_add_value_int(root, "Max Recorded Temperature",
+ le32_to_cpu(perf->max_recorded_temp));
+ json_object_add_value_int(root, "Lifetime Retired Block Count",
+ le32_to_cpu(perf->lifetime_retired_block_count));
+ json_object_add_value_int(root, "Lifetime Read Disturb Reallocation Events",
+ le32_to_cpu(perf->lifetime_read_disturb_realloc_events));
+ json_object_add_value_int(root, "Lifetime NAND Writes",
+ le64_to_cpu(perf->lifetime_nand_writes));
+ json_object_add_value_int(root, "Capacitor Health",
+ le32_to_cpu(perf->capacitor_health));
+ json_object_add_value_int(root, "Lifetime User Writes",
+ le64_to_cpu(perf->lifetime_user_writes));
+ json_object_add_value_int(root, "Lifetime User Reads",
+ le64_to_cpu(perf->lifetime_user_reads));
+ json_object_add_value_int(root, "Lifetime Thermal Throttle Activations",
+ le32_to_cpu(perf->lifetime_thermal_throttle_act));
+ json_object_add_value_int(root, "Percentage of P/E Cycles Remaining",
+ le32_to_cpu(perf->percentage_pe_cycles_remaining));
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static void wdc_get_commit_action_bin(__u8 commit_action_type, char *action_bin)
+{
+
+ switch (commit_action_type)
+ {
+ case(0):
+ strcpy(action_bin, "000b");
+ break;
+ case(1):
+ strcpy(action_bin, "001b");
+ break;
+ case(2):
+ strcpy(action_bin, "010b");
+ break;
+ case(3):
+ strcpy(action_bin, "011b");
+ break;
+ case(4):
+ strcpy(action_bin, "100b");
+ break;
+ case(5):
+ strcpy(action_bin, "101b");
+ break;
+ case(6):
+ strcpy(action_bin, "110b");
+ break;
+ case(7):
+ strcpy(action_bin, "111b");
+ break;
+ default:
+ strcpy(action_bin, "INVALID");
+ }
+
+}
+
+static void wdc_print_fw_act_history_log_normal(__u8 *data, int num_entries, __u32 cust_id, __u32 vendor_id)
+{
+ int i, j;
+ char previous_fw[9];
+ char new_fw[9];
+ char commit_action_bin[8];
+ char time_str[11];
+ __u16 oldestEntryIdx = 0, entryIdx = 0;
+ char *null_fw = "--------";
+ 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 || vendor_id == WDC_NVME_SNDK_VID) {
+ 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);
+
+ oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES;
+ if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) {
+ /* find lowest/oldest entry */
+ for (i = 0; i < num_entries; i++) {
+ j = (i+1 == WDC_MAX_NUM_ACT_HIST_ENTRIES) ? 0 : i+1;
+ if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) >
+ le16_to_cpu(fw_act_history_entry->entry[j].fw_act_hist_entries)) {
+ oldestEntryIdx = j;
+ break;
+ }
+ }
+ }
+ 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->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 if(vendor_id == WDC_NVME_SNDK_VID) {
+ printf(" ");
+ uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp));
+ memset((void *)time_str, 0, 9);
+ sprintf((char *)time_str, "%04d:%02d:%02d", (int)((timestamp/(3600*1000))%24), (int)((timestamp/(1000*60))%60),
+ (int)((timestamp/1000)%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("%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));
+
+ oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES;
+ 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++) {
+ 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(__u8 *data, int num_entries, __u32 cust_id, __u32 vendor_id)
+{
+ struct json_object *root;
+ int i, j;
+ char previous_fw[9];
+ char new_fw[9];
+ char commit_action_bin[8];
+ char fail_str[32];
+ 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, 11);
+ memset((void *)fail_str, 0, 11);
+ char *null_fw = "--------";
+ __u16 oldestEntryIdx = 0, entryIdx = 0;
+
+ root = json_create_object();
+
+ 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);
+
+ oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES;
+ if (num_entries == WDC_MAX_NUM_ACT_HIST_ENTRIES) {
+ /* find lowest/oldest entry */
+ for (i = 0; i < num_entries; i++) {
+ j = (i+1 == WDC_MAX_NUM_ACT_HIST_ENTRIES) ? 0 : i+1;
+ if (le16_to_cpu(fw_act_history_entry->entry[i].fw_act_hist_entries) >
+ le16_to_cpu(fw_act_history_entry->entry[j].fw_act_hist_entries)) {
+ oldestEntryIdx = j;
+ 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->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 if (vendor_id == WDC_NVME_SNDK_VID) {
+ uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp));
+ sprintf((char *)time_str, "%04d:%02d:%02d", (int)((timestamp/(3600*1000))%24), (int)((timestamp/(1000*60))%60),
+ (int)((timestamp/1000)%60));
+ json_object_add_value_string(root, "Power on Hour", time_str);
+ } else {
+ uint64_t timestamp = (0x0000FFFFFFFFFFFF & le64_to_cpu(fw_act_history_entry->entry[entryIdx].timestamp));
+ json_object_add_value_uint64(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);
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ 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));
+
+ oldestEntryIdx = WDC_MAX_NUM_ACT_HIST_ENTRIES;
+ 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);
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ entryIdx++;
+ if (entryIdx >= WDC_MAX_NUM_ACT_HIST_ENTRIES)
+ entryIdx = 0;
+ }
+ }
+
+ json_free_object(root);
+}
+
+static int nvme_get_ext_smart_cloud_log(int fd, __u8 **data, int uuid_index, __u32 namespace_id)
+{
+ int ret, i;
+ __u8 *log_ptr = NULL;
+
+ if ((log_ptr = (__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 */
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .lid = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID,
+ .nsid = namespace_id,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_index,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = WDC_NVME_SMART_CLOUD_ATTR_LEN,
+ .log = log_ptr,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args);
+
+ if (ret == 0) {
+
+ /* Verify GUID matches */
+ for (i = 0; i < WDC_C0_GUID_LENGTH; i++) {
+ if (ext_smart_guid[i] != *&log_ptr[SCAO_V1_LPG + i]) {
+ fprintf(stderr, "ERROR : WDC : Unknown GUID in C0 Log Page V1 data\n");
+ int j;
+ fprintf(stderr, "ERROR : WDC : Expected GUID: 0x");
+ for (j = 0; j < WDC_C0_GUID_LENGTH; j++) {
+ fprintf(stderr, "%x", ext_smart_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x");
+ for (j = 0; j < WDC_C0_GUID_LENGTH; j++) {
+ fprintf(stderr, "%x", *&log_ptr[SCAO_V1_LPG + j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ *data = log_ptr;
+
+ return ret;
+}
+
+
+static int nvme_get_hw_rev_log(int fd, __u8 **data, int uuid_index, __u32 namespace_id)
+{
+ int ret, i;
+ wdc_nvme_hw_rev_log *log_ptr = NULL;
+
+ if ((log_ptr = (wdc_nvme_hw_rev_log *)malloc(sizeof (__u8) * WDC_NVME_HW_REV_LOG_PAGE_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+
+ /* Get the 0xC0 log data */
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = fd,
+ .lid = WDC_NVME_GET_HW_REV_LOG_OPCODE,
+ .nsid = namespace_id,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_index,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = WDC_NVME_HW_REV_LOG_PAGE_LEN,
+ .log = log_ptr,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args);
+
+ if (ret == 0) {
+
+ /* Verify GUID matches */
+ for (i = 0; i < WDC_NVME_C6_GUID_LENGTH; i++) {
+ if (hw_rev_log_guid[i] != log_ptr->hw_rev_guid[i]) {
+ fprintf(stderr, "ERROR : WDC : Unknown GUID in HW Revision Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : WDC : Expected GUID: 0x");
+ for (j = 0; j < WDC_NVME_C6_GUID_LENGTH; j++) {
+ fprintf(stderr, "%x", hw_rev_log_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x");
+ for (j = 0; j < WDC_NVME_C6_GUID_LENGTH; j++) {
+ fprintf(stderr, "%x", log_ptr->hw_rev_guid[j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ *data = (__u8 *)log_ptr;
+
+ return ret;
+}
+
+
+static void wdc_print_hw_rev_log_normal(void *data)
+{
+ int i;
+ wdc_nvme_hw_rev_log *log_data = (wdc_nvme_hw_rev_log *)data;
+
+ printf(" Hardware Revision Log:- \n");
+
+ printf(" Global Device HW Revision : %d\n",
+ log_data->hw_rev_gdr);
+ printf(" ASIC HW Revision : %d\n",
+ log_data->hw_rev_ar);
+ printf(" PCB Manufacturer Code : %d\n",
+ log_data->hw_rev_pbc_mc);
+ printf(" DRAM Manufacturer Code : %d\n",
+ log_data->hw_rev_dram_mc);
+ printf(" NAND Manufacturer Code : %d\n",
+ log_data->hw_rev_nand_mc);
+ printf(" PMIC 1 Manufacturer Code : %d\n",
+ log_data->hw_rev_pmic1_mc);
+ printf(" PMIC 2 Manufacturer Code : %d\n",
+ log_data->hw_rev_pmic2_mc);
+ printf(" Other Component 1 Manf Code : %d\n",
+ log_data->hw_rev_c1_mc);
+ printf(" Other Component 2 Manf Code : %d\n",
+ log_data->hw_rev_c2_mc);
+ printf(" Other Component 3 Manf Code : %d\n",
+ log_data->hw_rev_c3_mc);
+ printf(" Other Component 4 Manf Code : %d\n",
+ log_data->hw_rev_c4_mc);
+ printf(" Other Component 5 Manf Code : %d\n",
+ log_data->hw_rev_c5_mc);
+ printf(" Other Component 6 Manf Code : %d\n",
+ log_data->hw_rev_c6_mc);
+ printf(" Other Component 7 Manf Code : %d\n",
+ log_data->hw_rev_c7_mc);
+ printf(" Other Component 8 Manf Code : %d\n",
+ log_data->hw_rev_c8_mc);
+ printf(" Other Component 9 Manf Code : %d\n",
+ log_data->hw_rev_c9_mc);
+
+ printf(" Device Manf Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_dev_mdi[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" ASIC Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_asic_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" PCB Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_pcb_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" DRAM Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_dram_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" NAND Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_nand_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" PMIC 1 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_pmic1_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" PMIC 2 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_pmic2_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 1 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c1_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 2 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c2_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 3 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c3_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 4 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c4_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 5 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c5_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 6 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c6_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 7 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c7_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 8 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c8_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Component 9 Detailed Info : 0x");
+ for (i = 0; i < 16; i++) {
+ printf("%02x", log_data->hw_rev_c9_di[i]);
+ if (i == 7)
+ printf(" 0x");
+ }
+ printf("\n");
+ printf(" Serial Number : 0x");
+ for (i = 0; i < 32; i++) {
+ if ((i > 1) & !(i % 8))
+ printf(" 0x");
+ printf("%02x", log_data->hw_rev_sn[i]);
+ }
+ printf("\n");
+
+ printf(" Log Page Version : %d\n",
+ log_data->hw_rev_version);
+ printf(" Log page GUID : 0x");
+ printf("%"PRIx64"%"PRIx64"\n",le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[0]));
+ printf("\n");
+}
+
+static void wdc_print_hw_rev_log_json(void *data)
+{
+ wdc_nvme_hw_rev_log *log_data = (wdc_nvme_hw_rev_log *)data;
+ struct json_object *root;
+ char json_data[80];
+
+ root = json_create_object();
+ json_object_add_value_uint(root, "Global Device HW Revision",
+ log_data->hw_rev_gdr);
+ json_object_add_value_uint(root, "ASIC HW Revision",
+ log_data->hw_rev_ar);
+ json_object_add_value_uint(root, "PCB Manufacturer Code",
+ log_data->hw_rev_pbc_mc);
+ json_object_add_value_uint(root, "DRAM Manufacturer Code",
+ log_data->hw_rev_dram_mc);
+ json_object_add_value_uint(root, "NAND Manufacturer Code",
+ log_data->hw_rev_nand_mc);
+ json_object_add_value_uint(root, "PMIC 1 Manufacturer Code",
+ log_data->hw_rev_pmic1_mc);
+ json_object_add_value_uint(root, "PMIC 2 Manufacturer Code",
+ log_data->hw_rev_pmic2_mc);
+ json_object_add_value_uint(root, "Other Component 1 Manf Code",
+ log_data->hw_rev_c1_mc);
+ json_object_add_value_uint(root, "Other Component 2 Manf Code",
+ log_data->hw_rev_c2_mc);
+ json_object_add_value_uint(root, "Other Component 3 Manf Code",
+ log_data->hw_rev_c3_mc);
+ json_object_add_value_uint(root, "Other Component 4 Manf Code",
+ log_data->hw_rev_c4_mc);
+ json_object_add_value_uint(root, "Other Component 5 Manf Code",
+ log_data->hw_rev_c5_mc);
+ json_object_add_value_uint(root, "Other Component 6 Manf Code",
+ log_data->hw_rev_c6_mc);
+ json_object_add_value_uint(root, "Other Component 7 Manf Code",
+ log_data->hw_rev_c7_mc);
+ json_object_add_value_uint(root, "Other Component 8 Manf Code",
+ log_data->hw_rev_c8_mc);
+ json_object_add_value_uint(root, "Other Component 9 Manf Code",
+ log_data->hw_rev_c9_mc);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dev_mdi[0]));
+ json_object_add_value_string(root, "Device Manf Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_asic_di[0]));
+ json_object_add_value_string(root, "ASIC Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pcb_di[0]));
+ json_object_add_value_string(root, "PCB Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_dram_di[0]));
+ json_object_add_value_string(root, "DRAM Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_nand_di[0]));
+ json_object_add_value_string(root, "NAND Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic1_di[0]));
+ json_object_add_value_string(root, "PMIC 1 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_pmic2_di[0]));
+ json_object_add_value_string(root, "PMIC 2 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c1_di[0]));
+ json_object_add_value_string(root, "Component 1 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c2_di[0]));
+ json_object_add_value_string(root, "Component 2 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c3_di[0]));
+ json_object_add_value_string(root, "Component 3 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c4_di[0]));
+ json_object_add_value_string(root, "Component 4 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c5_di[0]));
+ json_object_add_value_string(root, "Component 5 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c6_di[0]));
+ json_object_add_value_string(root, "Component 6 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c7_di[0]));
+ json_object_add_value_string(root, "Component 7 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c8_di[0]));
+ json_object_add_value_string(root, "Component 8 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_c9_di[0]));
+ json_object_add_value_string(root, "Component 9 Detailed Info", json_data);
+
+ memset((void*)json_data, 0, 80);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"%"PRIx64"%"PRIx64"",
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[0]), le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[16]), le64_to_cpu(*(uint64_t *)&log_data->hw_rev_sn[24]));
+ json_object_add_value_string(root, "Serial Number", json_data);
+
+ json_object_add_value_uint(root, "Log Page Version",
+ le16_to_cpu(log_data->hw_rev_version));
+
+ memset((void*)json_data, 0, 40);
+ sprintf((char*)json_data, "0x%"PRIx64"%"PRIx64"", le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[8]),
+ le64_to_cpu(*(uint64_t *)&log_data->hw_rev_guid[0]));
+ json_object_add_value_string(root, "Log Page GUID", json_data);
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static void wdc_print_ext_smart_cloud_log_normal(void *data, int mask)
+{
+ int i;
+ wdc_nvme_ext_smart_log *ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data;
+
+ if (mask == WDC_SCA_V1_NAND_STATS)
+ printf(" NAND Statistics :- \n");
+ else
+ printf(" SMART Cloud Attributes :- \n");
+
+ printf(" Physical Media Units Written TLC (Bytes) : %s\n",
+ uint128_t_to_string(le128_to_cpu(
+ ext_smart_log_ptr->ext_smart_pmuwt)));
+ printf(" Physical Media Units Written SLC (Bytes) : %s\n",
+ uint128_t_to_string(le128_to_cpu(
+ ext_smart_log_ptr->ext_smart_pmuws)));
+ printf(" Bad User NAND Block Count (Normalized) (Int) : %d\n",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bunbc));
+ printf(" Bad User NAND Block Count (Raw) (Int) : %"PRIu64"\n",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bunbc & 0xFFFFFFFFFFFF0000));
+ printf(" XOR Recovery Count (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_xrc));
+ printf(" Uncorrectable Read Error Count (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_urec));
+ if (mask == WDC_SCA_V1_ALL) {
+ printf(" SSD End to End correction counts (Corrected Errors) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_eece));
+ printf(" SSD End to End correction counts (Detected Errors) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_eede));
+ printf(" SSD End to End correction counts (Uncorrected E2E Errors) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_eeue));
+ printf(" System Data %% life-used : %d %%\n",
+ ext_smart_log_ptr->ext_smart_sdpu);
+ }
+ printf(" User data erase counts (Minimum TLC) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mnudec));
+ printf(" User data erase counts (Maximum TLC) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mxudec));
+ printf(" User data erase counts (Minimum SLC) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mnec));
+ printf(" User data erase counts (Maximum SLC) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mxec));
+ printf(" User data erase counts (Average SLC) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_avec));
+ printf(" User data erase counts (Average TLC) (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_avudec));
+ printf(" Program Fail Count (Normalized) (Int) : %d\n",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_pfc));
+ printf(" Program Fail Count (Raw) (Int) : %"PRIu64"\n",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_pfc & 0xFFFFFFFFFFFF0000));
+ printf(" Erase Fail Count (Normalized) (Int) : %d\n",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_efc));
+ printf(" Erase Fail Count (Raw) (Int) : %"PRIu64"\n",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_efc & 0xFFFFFFFFFFFF0000));
+ if (mask == WDC_SCA_V1_ALL) {
+ printf(" PCIe Correctable Error Count (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_pcec));
+ printf(" %% Free Blocks (User) (Int) : %d %%\n",
+ ext_smart_log_ptr->ext_smart_pfbu);
+ printf(" Security Version Number (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_svn));
+ printf(" %% Free Blocks (System) (Int) : %d %%\n",
+ ext_smart_log_ptr->ext_smart_pfbs);
+ printf(" NVMe Stats (# Data Set Management/TRIM Commands Completed) (Int) : %s\n",
+ uint128_t_to_string(le128_to_cpu(
+ ext_smart_log_ptr->ext_smart_dcc)));
+ printf(" Total Namespace Utilization (nvme0n1 NUSE) (Bytes) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_tnu));
+ printf(" NVMe Stats (# NVMe Format Commands Completed) (Int) : %d\n",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_fcc));
+ printf(" Background Back-Pressure Gauge(%%) (Int) : %d\n",
+ ext_smart_log_ptr->ext_smart_bbpg);
+ }
+ printf(" Total # of Soft ECC Error Count (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_seec));
+ if (mask == WDC_SCA_V1_ALL) {
+ printf(" Total # of Read Refresh Count (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_rfsc));
+ }
+ printf(" Bad System NAND Block Count (Normalized) (Int) : %d\n",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bsnbc));
+ printf(" Bad System NAND Block Count (Raw) (Int) : %"PRIu64"\n",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bsnbc & 0xFFFFFFFFFFFF0000));
+ printf(" Endurance Estimate (Total Writable Lifetime Bytes) (Bytes) : %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(ext_smart_log_ptr->ext_smart_eest)));
+ if (mask == WDC_SCA_V1_ALL) {
+ printf(" Thermal Throttling Status & Count (Number of thermal throttling events) (Int) : %d\n",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_ttc));
+ printf(" Total # Unaligned I/O (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_uio));
+ }
+ printf(" Total Physical Media Units Read (Bytes) (Int) : %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(ext_smart_log_ptr->ext_smart_pmur)));
+ if (mask == WDC_SCA_V1_ALL) {
+ printf(" Command Timeout (# of READ Commands > 5 Seconds) (Int) : %"PRIu32"\n",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_rtoc));
+ printf(" Command Timeout (# of WRITE Commands > 5 Seconds) (Int) : %"PRIu32"\n",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_wtoc));
+ printf(" Command Timeout (# of TRIM Commands > 5 Seconds) (Int) : %"PRIu32"\n",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_ttoc));
+ printf(" Total PCIe Link Retraining Count (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_plrc));
+ printf(" Active Power State Change Count (Int) : %"PRIu64"\n",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_pscc));
+ }
+ printf(" Cloud Boot SSD Spec Version (Int) : %d.%d.%d.%d\n",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_maj),
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_min),
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_pt),
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_err));
+ printf(" Cloud Boot SSD HW Revision (Int) : %d.%d.%d.%d\n",
+ 0, 0, 0, 0);
+ if (mask == WDC_SCA_V1_ALL) {
+ printf(" FTL Unit Size : %"PRIu32"\n",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_ftlus));
+ printf(" TCG Ownership Status : %"PRIu32"\n",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_tcgos));
+ printf(" Log Page Version (Int) : %d\n",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_lpv));
+ printf(" Log page GUID (Hex) : 0x");
+ for (i = WDC_C0_GUID_LENGTH; i > 0; i--)
+ printf("%02x", ext_smart_log_ptr->ext_smart_lpg[i-1]);
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static void wdc_print_ext_smart_cloud_log_json(void *data, int mask)
+{
+ wdc_nvme_ext_smart_log *ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data;
+ struct json_object *root;
+
+ root = json_create_object();
+ json_object_add_value_uint128(root, "physical_media_units_bytes_tlc",
+ le128_to_cpu(ext_smart_log_ptr->ext_smart_pmuwt));
+ json_object_add_value_uint128(root, "physical_media_units_bytes_slc",
+ le128_to_cpu(ext_smart_log_ptr->ext_smart_pmuws));
+ json_object_add_value_uint(root, "bad_user_blocks_normalized",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bunbc));
+ json_object_add_value_uint64(root, "bad_user_blocks_raw",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bunbc & 0xFFFFFFFFFFFF0000));
+ json_object_add_value_uint64(root, "xor_recovery_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_xrc));
+ json_object_add_value_uint64(root, "uncorrectable_read_errors",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_urec));
+ if (mask == WDC_SCA_V1_ALL) {
+ json_object_add_value_uint64(root, "corrected_e2e_errors",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_eece));
+ json_object_add_value_uint64(root, "detected_e2e_errors",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_eede));
+ json_object_add_value_uint64(root, "uncorrected_e2e_errors",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_eeue));
+ json_object_add_value_uint(root, "system_data_life_used_pct",
+ (__u8)ext_smart_log_ptr->ext_smart_sdpu);
+ }
+ json_object_add_value_uint64(root, "min_slc_user_data_erase_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mnec));
+ json_object_add_value_uint64(root, "min_tlc_user_data_erase_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mnudec));
+ json_object_add_value_uint64(root, "max_slc_user_data_erase_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mxec));
+ json_object_add_value_uint64(root, "max_tlc_user_data_erase_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_mxudec));
+ json_object_add_value_uint64(root, "avg_slc_user_data_erase_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_avec));
+ json_object_add_value_uint64(root, "avg_tlc_user_data_erase_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_avudec));
+ json_object_add_value_uint(root, "program_fail_count_normalized",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_pfc));
+ json_object_add_value_uint64(root, "program_fail_count_raw",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_pfc & 0xFFFFFFFFFFFF0000));
+ json_object_add_value_uint(root, "erase_fail_count_normalized",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_efc));
+ json_object_add_value_uint64(root, "erase_fail_count_raw",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_efc & 0xFFFFFFFFFFFF0000));
+ if (mask == WDC_SCA_V1_ALL) {
+ json_object_add_value_uint64(root, "pcie_correctable_errors",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_pcec));
+ json_object_add_value_uint(root, "pct_free_blocks_user",
+ (__u8)ext_smart_log_ptr->ext_smart_pfbu);
+ json_object_add_value_uint64(root, "security_version",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_svn));
+ json_object_add_value_uint(root, "pct_free_blocks_system",
+ (__u8)ext_smart_log_ptr->ext_smart_pfbs);
+ json_object_add_value_uint128(root, "num_of_trim_commands",
+ le128_to_cpu(ext_smart_log_ptr->ext_smart_dcc));
+ json_object_add_value_uint64(root, "total_nuse_bytes",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_tnu));
+ json_object_add_value_uint(root, "num_of_format_commands",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_fcc));
+ json_object_add_value_uint(root, "background_pressure_gauge",
+ (__u8)ext_smart_log_ptr->ext_smart_bbpg);
+ }
+ json_object_add_value_uint64(root, "soft_ecc_error_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_seec));
+ if (mask == WDC_SCA_V1_ALL) {
+ json_object_add_value_uint64(root, "read_refresh_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_rfsc));
+ }
+ json_object_add_value_uint(root, "bad_system_block_normalized",
+ le16_to_cpu(*(uint16_t *)ext_smart_log_ptr->ext_smart_bsnbc));
+ json_object_add_value_uint64(root, "bad_system_block_raw",
+ le64_to_cpu(*(uint64_t *)ext_smart_log_ptr->ext_smart_bsnbc & 0xFFFFFFFFFFFF0000));
+ json_object_add_value_uint128(root, "endurance_est_bytes",
+ le128_to_cpu(ext_smart_log_ptr->ext_smart_eest));
+ if (mask == WDC_SCA_V1_ALL) {
+ json_object_add_value_uint(root, "num_throttling_events",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_ttc));
+ json_object_add_value_uint64(root, "total_unaligned_io",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_uio));
+ }
+ json_object_add_value_uint128(root, "physical_media_units_read_bytes",
+ le128_to_cpu(ext_smart_log_ptr->ext_smart_pmur));
+ if (mask == WDC_SCA_V1_ALL) {
+ json_object_add_value_uint(root, "num_read_timeouts",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_rtoc));
+ json_object_add_value_uint(root, "num_write_timeouts",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_wtoc));
+ json_object_add_value_uint(root, "num_trim_timeouts",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_ttoc));
+ json_object_add_value_uint64(root, "pcie_link_retrain_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_plrc));
+ json_object_add_value_uint64(root, "active_power_state_change_count",
+ le64_to_cpu(ext_smart_log_ptr->ext_smart_pscc));
+ }
+ char vers_str[40];
+ memset((void*)vers_str, 0, 40);
+ sprintf((char*)vers_str, "%d.%d.%d.%d",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_maj), le16_to_cpu(ext_smart_log_ptr->ext_smart_min),
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_pt), le16_to_cpu(ext_smart_log_ptr->ext_smart_err));
+ json_object_add_value_string(root, "cloud_boot_ssd_spec_ver", vers_str);
+ memset((void*)vers_str, 0, 40);
+ sprintf((char*)vers_str, "%d.%d.%d.%d", 0, 0, 0, 0);
+ json_object_add_value_string(root, "cloud_boot_ssd_hw_ver", vers_str);
+
+ if (mask == WDC_SCA_V1_ALL) {
+ json_object_add_value_uint(root, "ftl_unit_size",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_ftlus));
+ json_object_add_value_uint(root, "tcg_ownership_status",
+ le32_to_cpu(ext_smart_log_ptr->ext_smart_tcgos));
+ json_object_add_value_uint(root, "log_page_ver",
+ le16_to_cpu(ext_smart_log_ptr->ext_smart_lpv));
+ char guid[40];
+ memset((void*)guid, 0, 40);
+ sprintf((char*)guid, "0x%"PRIx64"%"PRIx64"",le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[8]),
+ le64_to_cpu(*(uint64_t *)&ext_smart_log_ptr->ext_smart_lpg[0]));
+ json_object_add_value_string(root, "log_page_guid", guid);
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+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 : %s\n",
+ uint128_t_to_string(le128_to_cpu(&log_data[SCAO_PMUW])));
+ printf(" Physical media units read : %s\n",
+ uint128_t_to_string(le128_to_cpu(&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 : %s\n",
+ uint128_t_to_string(le128_to_cpu(&log_data[SCAO_PSC])));
+ printf(" Endurance estimate : %s\n",
+ uint128_t_to_string(le128_to_cpu(&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("%"PRIx64"%"PRIx64"\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]));
+ }
+ if (smart_log_ver > 3) {
+ printf(" Power State Change Count : %"PRIu64"\n",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC]));
+ }
+ 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_uint128(root, "Physical media units written",
+ le128_to_cpu(&log_data[SCAO_PMUW]));
+ json_object_add_value_uint128(root, "Physical media units read",
+ le128_to_cpu(&log_data[SCAO_PMUR]));
+ json_object_add_value_uint64(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_uint64(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_uint64(root, "XOR recovery count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_XRC]));
+ json_object_add_value_uint64(root, "Uncorrectable read error count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UREC]));
+ json_object_add_value_uint64(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_uint64(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_uint64(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_uint64(root, "Unaligned I/O",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_UIO]));
+ json_object_add_value_uint64(root, "Security Version Number",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_SVN]));
+ json_object_add_value_uint64(root, "NUSE - Namespace utilization",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_NUSE]));
+ json_object_add_value_uint128(root, "PLP start count",
+ le128_to_cpu(&log_data[SCAO_PSC]));
+ json_object_add_value_uint128(root, "Endurance estimate",
+ le128_to_cpu(&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%"PRIx64"%"PRIx64"",(uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG + 8]),
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_LPG]));
+ 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_uint64(root, "PCIe Link Retraining Count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PLRC]));
+ }
+ if(smart_log_ver > 3) {
+ json_object_add_value_uint64(root, "Power State Change Count",
+ (uint64_t)le64_to_cpu(*(uint64_t *)&log_data[SCAO_PSCC]));
+ }
+ 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_ext_smart_cloud_log(void *data, int fmt)
+{
+ if (!data) {
+ fprintf(stderr, "ERROR : WDC : Invalid buffer to read 0xC0 V1 log\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_ext_smart_cloud_log_normal(data, WDC_SCA_V1_ALL);
+ break;
+ case JSON:
+ wdc_print_ext_smart_cloud_log_json(data, WDC_SCA_V1_ALL);
+ break;
+ }
+ return 0;
+}
+
+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(nvme_root_t r, struct nvme_dev *dev, char *format,
+ int uuid_index, __u32 namespace_id)
+{
+ 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(r, dev))
+ 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(r, dev, &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:
+ case WDC_NVME_SN860_DEV_ID:
+ 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:
+ case WDC_NVME_SN650_DEV_ID_4:
+ case WDC_NVME_SN655_DEV_ID:
+ case WDC_NVME_SN560_DEV_ID_1:
+ case WDC_NVME_SN560_DEV_ID_2:
+ case WDC_NVME_SN560_DEV_ID_3:
+ case WDC_NVME_SN550_DEV_ID:
+ cust_id = wdc_get_fw_cust_id(r, dev);
+ if (cust_id == WDC_INVALID_CUSTOMER_ID) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid customer id\n", __func__);
+ return -1;
+ }
+
+ if ((cust_id == WDC_CUSTOMER_ID_0x1004) || (cust_id == WDC_CUSTOMER_ID_0x1008) || (cust_id == WDC_CUSTOMER_ID_0x1005))
+ {
+ 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;
+ }
+
+ if (namespace_id == NVME_NSID_ALL) {
+ ret = nvme_get_nsid(dev_fd(dev),
+ &namespace_id);
+ if (ret < 0) {
+ namespace_id = NVME_NSID_ALL;
+ }
+ }
+
+ /* Get the 0xC0 log data */
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID,
+ .nsid = namespace_id,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_index,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = WDC_NVME_SMART_CLOUD_ATTR_LEN,
+ .log = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args);
+
+ if (strcmp(format, "json"))
+ nvme_show_status(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 */
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
+ .nsid = NVME_NSID_ALL,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_index,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = WDC_NVME_EOL_STATUS_LOG_LEN,
+ .log = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args);
+
+ if (strcmp(format, "json"))
+ nvme_show_status(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_simple(dev_fd(dev),
+ WDC_NVME_GET_EOL_STATUS_LOG_OPCODE,
+ WDC_NVME_EOL_STATUS_LOG_LEN,
+ data);
+
+ if (strcmp(format, "json"))
+ nvme_show_status(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_simple(dev_fd(dev),
+ WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID,
+ WDC_NVME_SMART_CLOUD_ATTR_LEN, data);
+
+ if (strcmp(format, "json"))
+ nvme_show_status(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;
+
+ case WDC_NVME_SN820CL_DEV_ID:
+ /* Get the 0xC0 Extended Smart Cloud Attribute log data */
+ data = NULL;
+ ret = nvme_get_ext_smart_cloud_log(dev_fd(dev), &data,
+ uuid_index, namespace_id);
+
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ wdc_print_ext_smart_cloud_log(data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page V1 data\n");
+ ret = -1;
+ }
+
+ if (data)
+ 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_latency_monitor_log(struct nvme_dev *dev,
+ struct wdc_ssd_latency_monitor_log *log_data,
+ int fmt)
+{
+ if (!log_data) {
+ fprintf(stderr, "ERROR : WDC : Invalid C3 log data buffer\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_latency_monitor_log_normal(dev, log_data);
+ break;
+ case JSON:
+ wdc_print_latency_monitor_log_json(log_data);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_print_error_rec_log(struct wdc_ocp_c1_error_recovery_log *log_data, int fmt)
+{
+ if (!log_data) {
+ fprintf(stderr, "ERROR : WDC : Invalid C1 log data buffer\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_error_rec_log_normal(log_data);
+ break;
+ case JSON:
+ wdc_print_error_rec_log_json(log_data);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_print_dev_cap_log(struct wdc_ocp_C4_dev_cap_log *log_data, int fmt)
+{
+ if (!log_data) {
+ fprintf(stderr, "ERROR : WDC : Invalid C4 log data buffer\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_dev_cap_log_normal(log_data);
+ break;
+ case JSON:
+ wdc_print_dev_cap_log_json(log_data);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_print_unsupported_reqs_log(struct wdc_ocp_C5_unsupported_reqs *log_data, int fmt)
+{
+ if (!log_data) {
+ fprintf(stderr, "ERROR : WDC : Invalid C5 log data buffer\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_unsupported_reqs_log_normal(log_data);
+ break;
+ case JSON:
+ wdc_print_unsupported_reqs_log_json(log_data);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_print_fb_ca_log(struct wdc_ssd_ca_perf_stats *perf, int fmt)
+{
+ if (!perf) {
+ fprintf(stderr, "ERROR : WDC : Invalid buffer to read perf stats\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_fb_ca_log_normal(perf);
+ break;
+ case JSON:
+ wdc_print_fb_ca_log_json(perf);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_print_bd_ca_log(struct nvme_dev *dev, void *bd_data, int fmt)
+{
+ if (!bd_data) {
+ fprintf(stderr, "ERROR : WDC : Invalid buffer to read data\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_bd_ca_log_normal(dev, bd_data);
+ break;
+ case JSON:
+ wdc_print_bd_ca_log_json(bd_data);
+ break;
+ default:
+ fprintf(stderr, "ERROR : WDC : Unknown format - %d\n", fmt);
+ return -1;
+ }
+ return 0;
+}
+
+static int wdc_print_d0_log(struct wdc_ssd_d0_smart_log *perf, int fmt)
+{
+ if (!perf) {
+ fprintf(stderr, "ERROR : WDC : Invalid buffer to read perf stats\n");
+ return -1;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_d0_log_normal(perf);
+ break;
+ case JSON:
+ wdc_print_d0_log_json(perf);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_print_fw_act_history_log(__u8 *data, int num_entries, int fmt, __u32 cust_id, __u32 vendor_id)
+{
+ 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(data, num_entries, cust_id, vendor_id);
+ break;
+ case JSON:
+ wdc_print_fw_act_history_log_json(data, num_entries, cust_id, vendor_id);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_get_ca_log_page(nvme_root_t r, struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ struct wdc_ssd_ca_perf_stats *perf;
+ uint32_t read_device_id, read_vendor_id;
+ __u32 cust_id;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == false) {
+ fprintf(stderr, "ERROR : WDC : 0xCA Log Page not supported\n");
+ return -1;
+ }
+
+ /* get the FW customer id */
+ cust_id = wdc_get_fw_cust_id(r, dev);
+ if (cust_id == WDC_INVALID_CUSTOMER_ID) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid customer id\n", __func__);
+ return -1;
+ }
+
+ ret = wdc_get_pci_ids(r, dev, &read_device_id, &read_vendor_id);
+
+ switch (read_device_id) {
+
+ case WDC_NVME_SN200_DEV_ID:
+
+ if (cust_id == WDC_CUSTOMER_ID_0x1005) {
+
+ if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev),
+ WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+ WDC_FB_CA_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ perf = (struct wdc_ssd_ca_perf_stats *)(data);
+ ret = wdc_print_fb_ca_log(perf, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n");
+ ret = -1;
+ }
+ } else {
+
+ fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = 0x%x\n", cust_id);
+ return -1;
+ }
+ break;
+
+ 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:
+ case WDC_NVME_SN860_DEV_ID:
+ if (cust_id == WDC_CUSTOMER_ID_0x1005) {
+
+ if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(data, 0, sizeof (__u8) * WDC_FB_CA_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev),
+ WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+ WDC_FB_CA_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ perf = (struct wdc_ssd_ca_perf_stats *)(data);
+ ret = wdc_print_fb_ca_log(perf, fmt);
+ } else {
+ 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) ||
+ (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));
+ return -1;
+ }
+
+ memset(data, 0, sizeof (__u8) * WDC_BD_CA_LOG_BUF_LEN);
+ ret = nvme_get_log_simple(dev_fd(dev),
+ WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+ WDC_BD_CA_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ ret = wdc_print_bd_ca_log(dev, data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read CA Log Page data\n");
+ ret = -1;
+ }
+
+ break;
+ } else {
+
+ fprintf(stderr, "ERROR : WDC : Unsupported Customer id, id = 0x%x\n", cust_id);
+ return -1;
+ }
+ break;
+
+ default:
+
+ fprintf(stderr, "ERROR : WDC : Log page 0xCA not supported for this device\n");
+ return -1;
+ break;
+ }
+
+ free(data);
+ return ret;
+}
+
+static int wdc_get_c1_log_page(nvme_root_t r, struct nvme_dev *dev,
+ char *format, uint8_t interval)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ __u8 *p;
+ int i;
+ int skip_cnt = 4;
+ int total_subpages;
+ struct wdc_log_page_header *l;
+ struct wdc_log_page_subpage_header *sph;
+ struct wdc_ssd_perf_stats *perf;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ if (interval < 1 || interval > 15) {
+ fprintf(stderr, "ERROR : WDC : interval out of range [1-15]\n");
+ return -1;
+ }
+
+ if ((data = (__u8*) malloc(sizeof (__u8) * WDC_ADD_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_ADD_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), WDC_NVME_ADD_LOG_OPCODE,
+ WDC_ADD_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+ if (ret == 0) {
+ l = (struct wdc_log_page_header*)data;
+ total_subpages = l->num_subpages + WDC_NVME_GET_STAT_PERF_INTERVAL_LIFETIME - 1;
+ for (i = 0, p = data + skip_cnt; i < total_subpages; i++, p += skip_cnt) {
+ sph = (struct wdc_log_page_subpage_header *) p;
+ if (sph->spcode == WDC_GET_LOG_PAGE_SSD_PERFORMANCE) {
+ if (sph->pcset == interval) {
+ perf = (struct wdc_ssd_perf_stats *) (p + 4);
+ ret = wdc_print_log(perf, fmt);
+ break;
+ }
+ }
+ skip_cnt = le16_to_cpu(sph->subpage_length) + 4;
+ }
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : Unable to read data from buffer\n");
+ }
+ }
+ free(data);
+ return ret;
+}
+
+static int wdc_get_c3_log_page(nvme_root_t r, struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ int i;
+ struct wdc_ssd_latency_monitor_log *log_data;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ if ((data = (__u8 *) malloc(sizeof(__u8) * WDC_LATENCY_MON_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_LATENCY_MON_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), WDC_LATENCY_MON_LOG_ID,
+ WDC_LATENCY_MON_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+
+ if (ret == 0) {
+ log_data = (struct wdc_ssd_latency_monitor_log*)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != WDC_LATENCY_MON_VERSION) {
+ fprintf(stderr, "ERROR : WDC : invalid latency monitor version\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* check log page guid */
+ /* Verify GUID matches */
+ for (i=0; i<16; i++) {
+ if (wdc_lat_mon_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : WDC : Unknown GUID in C3 Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : WDC : Expected GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", wdc_lat_mon_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* parse the data */
+ wdc_print_latency_monitor_log(dev, log_data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read C3 data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+
+}
+
+static int wdc_get_ocp_c1_log_page(nvme_root_t r, struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ int i;
+ struct wdc_ocp_c1_error_recovery_log *log_data;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ if ((data = (__u8 *) malloc(sizeof(__u8) * WDC_ERROR_REC_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_ERROR_REC_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), WDC_ERROR_REC_LOG_ID,
+ WDC_ERROR_REC_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+
+ if (ret == 0) {
+ log_data = (struct wdc_ocp_c1_error_recovery_log *)data;
+
+ /* check log page version */
+ if ((log_data->log_page_version != WDC_ERROR_REC_LOG_VERSION1) &&
+ (log_data->log_page_version != WDC_ERROR_REC_LOG_VERSION2)) {
+ fprintf(stderr, "ERROR : WDC : invalid error recovery log version - %d\n", log_data->log_page_version);
+ ret = -1;
+ goto out;
+ }
+
+ /* Verify GUID matches */
+ for (i=0; i < WDC_OCP_C1_GUID_LENGTH; i++) {
+ if (wdc_ocp_c1_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : WDC : Unknown GUID in C1 Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : WDC : Expected GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", wdc_ocp_c1_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* parse the data */
+ wdc_print_error_rec_log(log_data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read error recovery (C1) data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int wdc_get_ocp_c4_log_page(nvme_root_t r, struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ int i;
+ struct wdc_ocp_C4_dev_cap_log *log_data;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ if ((data = (__u8 *) malloc(sizeof(__u8) * WDC_DEV_CAP_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_DEV_CAP_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), WDC_DEV_CAP_LOG_ID,
+ WDC_DEV_CAP_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+
+ if (ret == 0) {
+ log_data = (struct wdc_ocp_C4_dev_cap_log *)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != WDC_DEV_CAP_LOG_VERSION) {
+ fprintf(stderr, "ERROR : WDC : invalid device capabilities log version - %d\n", log_data->log_page_version);
+ ret = -1;
+ goto out;
+ }
+
+ /* Verify GUID matches */
+ for (i=0; i < WDC_OCP_C4_GUID_LENGTH; i++) {
+ if (wdc_ocp_c4_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : WDC : Unknown GUID in C4 Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : WDC : Expected GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", wdc_ocp_c4_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* parse the data */
+ wdc_print_dev_cap_log(log_data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read device capabilities (C4) data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int wdc_get_ocp_c5_log_page(nvme_root_t r, struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ int i;
+ struct wdc_ocp_C5_unsupported_reqs *log_data;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ if ((data = (__u8 *) malloc(sizeof(__u8) * WDC_UNSUPPORTED_REQS_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_UNSUPPORTED_REQS_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev), WDC_UNSUPPORTED_REQS_LOG_ID,
+ WDC_UNSUPPORTED_REQS_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret, false), ret);
+
+ if (ret == 0) {
+ log_data = (struct wdc_ocp_C5_unsupported_reqs *)data;
+
+ /* check log page version */
+ if (log_data->log_page_version != WDC_UNSUPPORTED_REQS_LOG_VERSION) {
+ fprintf(stderr, "ERROR : WDC : invalid unsupported requirements log version - %d\n", log_data->log_page_version);
+ ret = -1;
+ goto out;
+ }
+
+ /* Verify GUID matches */
+ for (i=0; i < WDC_OCP_C5_GUID_LENGTH; i++) {
+ if (wdc_ocp_c5_guid[i] != log_data->log_page_guid[i]) {
+ fprintf(stderr, "ERROR : WDC : Unknown GUID in C5 Log Page data\n");
+ int j;
+ fprintf(stderr, "ERROR : WDC : Expected GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", wdc_ocp_c5_guid[j]);
+ }
+ fprintf(stderr, "\nERROR : WDC : Actual GUID: 0x");
+ for (j = 0; j<16; j++) {
+ fprintf(stderr, "%x", log_data->log_page_guid[j]);
+ }
+ fprintf(stderr, "\n");
+
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* parse the data */
+ wdc_print_unsupported_reqs_log(log_data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read unsupported requirements (C5) data from buffer\n");
+ }
+
+out:
+ free(data);
+ return ret;
+}
+
+static int wdc_get_d0_log_page(nvme_root_t r, struct nvme_dev *dev, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ struct wdc_ssd_d0_smart_log *perf;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ /* verify the 0xD0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == false) {
+ fprintf(stderr, "ERROR : WDC : 0xD0 Log Page not supported\n");
+ return -1;
+ }
+
+ if ((data = (__u8*) malloc(sizeof (__u8) * WDC_NVME_VU_SMART_LOG_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+ memset(data, 0, sizeof (__u8) * WDC_NVME_VU_SMART_LOG_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev),
+ WDC_NVME_GET_VU_SMART_LOG_OPCODE,
+ WDC_NVME_VU_SMART_LOG_LEN, data);
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ perf = (struct wdc_ssd_d0_smart_log *)(data);
+ ret = wdc_print_d0_log(perf, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read D0 Log Page data\n");
+ ret = -1;
+ }
+
+ free(data);
+ return ret;
+}
+
+static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve additional performance statistics.";
+ const char *interval = "Interval to read the statistics from [1, 15].";
+ const char *log_page_version = "Log Page Version: 0 = vendor, 1 = WDC";
+ const char *log_page_mask = "Log Page Mask, comma separated list: 0xC0, 0xC1, 0xCA, 0xD0";
+ const char *namespace_id = "desired namespace id";
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ 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;
+ char *output_format;
+ __u8 log_page_version;
+ char *log_page_mask;
+ __u32 namespace_id;
+ };
+
+ struct config cfg = {
+ .interval = 14,
+ .output_format = "normal",
+ .log_page_version = 0,
+ .log_page_mask = "",
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ 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_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ 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(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_SMART_LOG_MASK) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ if (((capabilities & WDC_DRIVE_CAP_C0_LOG_PAGE) == WDC_DRIVE_CAP_C0_LOG_PAGE) &&
+ (page_mask & WDC_C0_PAGE_MASK)) {
+ /* Get 0xC0 log page if possible. */
+ ret = wdc_get_c0_log_page(r, dev, cfg.output_format,
+ uuid_index, cfg.namespace_id);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the C0 Log Page, ret = %d\n", ret);
+ }
+ if (((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(r, dev, 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) &&
+ (page_mask & WDC_C1_PAGE_MASK)) {
+ /* Get the C1 Log Page */
+ ret = wdc_get_c1_log_page(r, dev, 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) &&
+ (page_mask & WDC_D0_PAGE_MASK)) {
+ /* Get the D0 Log Page */
+ ret = wdc_get_d0_log_page(r, dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the D0 Log Page, ret = %d\n", ret);
+ }
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_vs_cloud_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve Cloud Log Smart/Health Information";
+ const char *namespace_id = "desired namespace id";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ int ret, fmt = -1;
+ nvme_root_t r;
+ __u8 *data;
+
+ struct config {
+ char *output_format;
+ __u32 namespace_id;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_CLOUD_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ data = NULL;
+ ret = nvme_get_ext_smart_cloud_log(dev_fd(dev), &data, 0,
+ cfg.namespace_id);
+
+ if (strcmp(cfg.output_format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC %s: invalid output format\n", __func__);
+ ret = fmt;
+ }
+
+ /* parse the data */
+ wdc_print_ext_smart_cloud_log(data, fmt);
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read C0 Log Page V1 data\n");
+ ret = -1;
+ }
+
+ if (data)
+ free(data);
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_vs_hw_rev_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve Hardware Revision Log Information";
+ const char *namespace_id = "desired namespace id";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ int ret, fmt = -1;
+ __u8 *data = NULL;
+ nvme_root_t r;
+
+ struct config {
+ char *output_format;
+ __u32 namespace_id;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_HW_REV_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = nvme_get_hw_rev_log(dev_fd(dev), &data, 0, cfg.namespace_id);
+
+ if (strcmp(cfg.output_format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC %s: invalid output format\n", __func__);
+ ret = fmt;
+ goto free_buf;
+ }
+
+ if (!data) {
+ fprintf(stderr, "ERROR : WDC : Invalid buffer to read Hardware Revision log\n");
+ ret = -1;
+ goto out;
+ }
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_hw_rev_log_normal(data);
+ break;
+ case JSON:
+ wdc_print_hw_rev_log_json(data);
+ break;
+ }
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read Hardware Revision Log Page data\n");
+ ret = -1;
+ }
+
+free_buf:
+ if (data)
+ free(data);
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_vs_device_waf(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve Device Write Amplication Factor";
+ const char *namespace_id = "desired namespace id";
+ struct nvme_smart_log smart_log;
+ struct nvme_dev *dev;
+ __u8 *data;
+ nvme_root_t r;
+ int ret = 0;
+ int fmt = -1;
+ __u64 capabilities = 0;
+ wdc_nvme_ext_smart_log *ext_smart_log_ptr;
+ long double data_units_written = 0,
+ phys_media_units_written_tlc = 0,
+ phys_media_units_written_slc = 0;
+ struct json_object *root = NULL;
+ char tlc_waf_str[32] = { 0 },
+ slc_waf_str[32] = { 0 };
+
+ struct config {
+ char *output_format;
+ __u32 namespace_id;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_DEVICE_WAF) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* get data units written from the smart log page */
+ ret = nvme_get_log_smart(dev_fd(dev), cfg.namespace_id, false,
+ &smart_log);
+ if (!ret) {
+ data_units_written = int128_to_double(smart_log.data_units_written);
+ }
+ else if (ret > 0) {
+ nvme_show_status(ret);
+ ret = -1;
+ goto out;
+ } else {
+ fprintf(stderr, "smart log: %s\n", nvme_strerror(errno));
+ ret = -1;
+ goto out;
+ }
+
+ /* get Physical Media Units Written from extended smart/C0 log page */
+ data = NULL;
+ ret = nvme_get_ext_smart_cloud_log(dev_fd(dev), &data, 0,
+ cfg.namespace_id);
+
+ if (ret == 0) {
+ ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data;
+ phys_media_units_written_tlc = int128_to_double(ext_smart_log_ptr->ext_smart_pmuwt);
+ phys_media_units_written_slc = int128_to_double(ext_smart_log_ptr->ext_smart_pmuws);
+
+ if (data)
+ free(data);
+ } else {
+ fprintf(stderr, "ERROR : WDC %s: get smart cloud log failure\n", __func__);
+ ret = -1;
+ goto out;
+ }
+
+ if (strcmp(cfg.output_format, "json"))
+ nvme_show_status(ret);
+
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC %s: invalid output format\n", __func__);
+ ret = fmt;
+ goto out;
+ }
+
+ if (data_units_written == 0) {
+ fprintf(stderr, "ERROR : WDC %s: 0 data units written\n", __func__);
+ ret = -1;
+ goto out;
+ }
+
+ if (fmt == NORMAL) {
+ printf("Device Write Amplification Factor TLC : %4.2Lf\n",
+ (phys_media_units_written_tlc/data_units_written));
+ printf("Device Write Amplification Factor SLC : %4.2Lf\n",
+ (phys_media_units_written_slc/data_units_written));
+ }
+ else if (fmt == JSON) {
+ root = json_create_object();
+ sprintf(tlc_waf_str, "%4.2Lf", (phys_media_units_written_tlc/data_units_written));
+ sprintf(slc_waf_str, "%4.2Lf", (phys_media_units_written_slc/data_units_written));
+
+ json_object_add_value_string(root, "Device Write Amplification Factor TLC", tlc_waf_str);
+ json_object_add_value_string(root, "Device Write Amplification Factor SLC", slc_waf_str);
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+ }
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_latency_monitor_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve latency monitor log data.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret = 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_C3_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = wdc_get_c3_log_page(r, dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the Latency Monitor (C3) Log Page, ret = %d\n", ret);
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_error_recovery_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve error recovery log data.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret = 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_OCP_C1_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = wdc_get_ocp_c1_log_page(r, dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the Error Recovery (C1) Log Page, ret = 0x%x\n", ret);
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_dev_capabilities_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve device capabilities log data.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret = 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_OCP_C4_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = wdc_get_ocp_c4_log_page(r, dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the Device Capabilities (C4) Log Page, ret = 0x%x\n", ret);
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_unsupported_reqs_log(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve unsupported requirements log data.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret = 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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_OCP_C5_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = wdc_get_ocp_c5_log_page(r, dev, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the Unsupported Requirements (C5) Log Page, ret = 0x%x\n", ret);
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ 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, NULL);
+ nvme_show_status(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, NULL);
+ nvme_show_status(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_features_simple(fd, WDC_NVME_CLEAR_PCIE_CORR_FEATURE_ID, 0, value,
+ false, &result);
+
+ nvme_show_status(ret);
+ return ret;
+}
+
+static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Clear PCIE Correctable Errors.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ if (!wdc_check_device(r, dev)) {
+ ret = -1;
+ goto out;
+ }
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ 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(dev_fd(dev));
+ }
+ else if (capabilities & WDC_DRIVE_CAP_VUC_CLEAR_PCIE) {
+ ret = wdc_do_clear_pcie_correctable_errors_vuc(dev_fd(dev));
+ }
+ else {
+ ret = wdc_do_clear_pcie_correctable_errors_fid(dev_fd(dev));
+ }
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_drive_status(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Get Drive Status.";
+ struct nvme_dev *dev;
+ int ret = 0;
+ nvme_root_t r;
+ __le32 system_eol_state;
+ __le32 user_eol_state;
+ __le32 format_corrupt_reason = cpu_to_le32(0xFFFFFFFF);
+ __le32 eol_status;
+ __le32 assert_status = cpu_to_le32(0xFFFFFFFF);
+ __le32 thermal_status = cpu_to_le32(0xFFFFFFFF);
+ __u64 capabilities = 0;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_DRIVE_STATUS) != WDC_DRIVE_CAP_DRIVE_STATUS) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* verify the 0xC2 Device Manageability log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev,
+ WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE) == false) {
+ fprintf(stderr, "ERROR : WDC : 0xC2 Log Page not supported\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* Get the assert dump present status */
+ if (!wdc_nvme_get_dev_status_log_data(r, dev, &assert_status,
+ WDC_C2_ASSERT_DUMP_PRESENT_ID))
+ fprintf(stderr, "ERROR : WDC : Get Assert Status Failed\n");
+
+ /* Get the thermal throttling status */
+ if (!wdc_nvme_get_dev_status_log_data(r, dev, &thermal_status,
+ WDC_C2_THERMAL_THROTTLE_STATUS_ID))
+ fprintf(stderr, "ERROR : WDC : Get Thermal Throttling Status Failed\n");
+
+ /* Get EOL status */
+ if (!wdc_nvme_get_dev_status_log_data(r, dev, &eol_status,
+ WDC_C2_USER_EOL_STATUS_ID)) {
+ fprintf(stderr, "ERROR : WDC : Get User EOL Status Failed\n");
+ eol_status = cpu_to_le32(-1);
+ }
+
+ /* Get Customer EOL state */
+ if (!wdc_nvme_get_dev_status_log_data(r, dev, &user_eol_state,
+ WDC_C2_USER_EOL_STATE_ID))
+ fprintf(stderr, "ERROR : WDC : Get User EOL State Failed\n");
+
+ /* Get System EOL state*/
+ if (!wdc_nvme_get_dev_status_log_data(r, dev, &system_eol_state,
+ WDC_C2_SYSTEM_EOL_STATE_ID))
+ fprintf(stderr, "ERROR : WDC : Get System EOL State Failed\n");
+
+ /* Get format corrupt reason*/
+ if (!wdc_nvme_get_dev_status_log_data(r, dev, &format_corrupt_reason,
+ WDC_C2_FORMAT_CORRUPT_REASON_ID))
+ fprintf(stderr, "ERROR : WDC : Get Format Corrupt Reason Failed\n");
+
+ printf(" Drive Status :- \n");
+ if ((int)le32_to_cpu(eol_status) >= 0) {
+ printf(" Percent Used: %"PRIu32"%%\n",
+ le32_to_cpu(eol_status));
+ }
+ else
+ printf(" Percent Used: Unknown\n");
+ if (system_eol_state == WDC_EOL_STATUS_NORMAL && user_eol_state == WDC_EOL_STATUS_NORMAL)
+ printf(" Drive Life Status: Normal\n");
+ else if (system_eol_state == WDC_EOL_STATUS_END_OF_LIFE || user_eol_state == WDC_EOL_STATUS_END_OF_LIFE)
+ printf(" Drive Life Status: End Of Life\n");
+ else if (system_eol_state == WDC_EOL_STATUS_READ_ONLY || user_eol_state == WDC_EOL_STATUS_READ_ONLY)
+ printf(" Drive Life Status: Read Only\n");
+ else
+ printf(" Drive Life Status: Unknown : 0x%08x/0x%08x\n",
+ le32_to_cpu(user_eol_state), le32_to_cpu(system_eol_state));
+
+ if (assert_status == WDC_ASSERT_DUMP_PRESENT)
+ printf(" Assert Dump Status: Present\n");
+ else if (assert_status == WDC_ASSERT_DUMP_NOT_PRESENT)
+ printf(" Assert Dump Status: Not Present\n");
+ else
+ printf(" Assert Dump Status: Unknown : 0x%08x\n", le32_to_cpu(assert_status));
+
+ if (thermal_status == WDC_THERMAL_THROTTLING_OFF)
+ printf(" Thermal Throttling Status: Off\n");
+ else if (thermal_status == WDC_THERMAL_THROTTLING_ON)
+ printf(" Thermal Throttling Status: On\n");
+ else if (thermal_status == WDC_THERMAL_THROTTLING_UNAVAILABLE)
+ printf(" Thermal Throttling Status: Unavailable\n");
+ else
+ printf(" Thermal Throttling Status: Unknown : 0x%08x\n", le32_to_cpu(thermal_status));
+
+ if (format_corrupt_reason == WDC_FORMAT_NOT_CORRUPT)
+ printf(" Format Corrupt Reason: Format Not Corrupted\n");
+ else if (format_corrupt_reason == WDC_FORMAT_CORRUPT_FW_ASSERT)
+ printf(" Format Corrupt Reason: Format Corrupt due to FW Assert\n");
+ else if (format_corrupt_reason == WDC_FORMAT_CORRUPT_UNKNOWN)
+ printf(" Format Corrupt Reason: Format Corrupt for Unknown Reason\n");
+ else
+ printf(" Format Corrupt Reason: Unknown : 0x%08x\n", le32_to_cpu(format_corrupt_reason));
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_clear_assert_dump(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Clear Assert Dump Present Status.";
+ struct nvme_dev *dev;
+ int ret = -1;
+ nvme_root_t r;
+ __le32 assert_status = cpu_to_le32(0xFFFFFFFF);
+ __u64 capabilities = 0;
+ struct nvme_passthru_cmd admin_cmd;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_CLEAR_ASSERT) != WDC_DRIVE_CAP_CLEAR_ASSERT) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+ if (!wdc_nvme_get_dev_status_log_data(r, dev, &assert_status,
+ WDC_C2_ASSERT_DUMP_PRESENT_ID)) {
+ fprintf(stderr, "ERROR : WDC : Get Assert Status Failed\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* Get the assert dump present status */
+ if (assert_status == WDC_ASSERT_DUMP_PRESENT) {
+ memset(&admin_cmd, 0, sizeof (admin_cmd));
+ admin_cmd.opcode = WDC_NVME_CLEAR_ASSERT_DUMP_OPCODE;
+ admin_cmd.cdw12 = ((WDC_NVME_CLEAR_ASSERT_DUMP_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_CLEAR_ASSERT_DUMP_CMD);
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd,
+ NULL);
+ nvme_show_status(ret);
+ } else
+ fprintf(stderr, "INFO : WDC : No Assert Dump Present\n");
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_fw_act_history(nvme_root_t r, struct nvme_dev *dev,
+ char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ struct wdc_fw_act_history_log_hdr *fw_act_history_hdr;
+
+ if (!wdc_check_device(r, dev))
+ return -1;
+
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ /* verify the FW Activate History log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID) == false) {
+ fprintf(stderr, "ERROR : WDC : %d Log Page not supported\n", WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID);
+ return -1;
+ }
+
+ if ((data = (__u8*) malloc(sizeof (__u8) * WDC_FW_ACT_HISTORY_LOG_BUF_LEN)) == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return -1;
+ }
+
+ memset(data, 0, sizeof (__u8) * WDC_FW_ACT_HISTORY_LOG_BUF_LEN);
+
+ ret = nvme_get_log_simple(dev_fd(dev),
+ WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID,
+ WDC_FW_ACT_HISTORY_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ fw_act_history_hdr = (struct wdc_fw_act_history_log_hdr *)(data);
+
+ if ((fw_act_history_hdr->num_entries > 0) && (fw_act_history_hdr->num_entries <= WDC_MAX_NUM_ACT_HIST_ENTRIES))
+ ret = wdc_print_fw_act_history_log(data, fw_act_history_hdr->num_entries, fmt, 0, 0);
+ 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 __u32 wdc_get_fw_cust_id(nvme_root_t r, struct nvme_dev *dev)
+{
+
+ __u32 cust_id = WDC_INVALID_CUSTOMER_ID;
+ __u32 *cust_id_ptr = NULL;
+
+ if (!(get_dev_mgment_cbs_data(r, dev, WDC_C2_CUSTOMER_ID_ID, (void*)&cust_id_ptr))) {
+ fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID);
+ } else {
+ cust_id = *cust_id_ptr;
+ }
+
+ free(cust_id_ptr);
+ return cust_id;
+}
+
+static int wdc_get_fw_act_history_C2(nvme_root_t r, struct nvme_dev *dev,
+ char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ __u32 cust_id;
+ struct wdc_fw_act_history_log_format_c2 *fw_act_history_log;
+ __u32 tot_entries = 0, num_entries = 0;
+ __u32 vendor_id = 0, device_id = 0;
+
+ if (!wdc_check_device(r, dev))
+ 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(r, dev, &device_id, &vendor_id);
+
+ 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_simple(dev_fd(dev),
+ WDC_NVME_GET_FW_ACT_HISTORY_C2_LOG_ID,
+ WDC_FW_ACT_HISTORY_C2_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ nvme_show_status(ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ fw_act_history_log = (struct wdc_fw_act_history_log_format_c2*)(data);
+ tot_entries = le32_to_cpu(fw_act_history_log->num_entries);
+
+ if (tot_entries > 0) {
+ /* get the FW customer id */
+ cust_id = wdc_get_fw_cust_id(r, dev);
+ if (cust_id == WDC_INVALID_CUSTOMER_ID) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid customer id\n", __func__);
+ ret = -1;
+ goto freeData;
+ }
+ num_entries = (tot_entries < WDC_MAX_NUM_ACT_HIST_ENTRIES) ? tot_entries :
+ WDC_MAX_NUM_ACT_HIST_ENTRIES;
+ ret = wdc_print_fw_act_history_log(data, num_entries, fmt, cust_id, vendor_id);
+ } else {
+ fprintf(stderr, "INFO : WDC : No FW Activate History entries found.\n");
+ ret = 0;
+ }
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n");
+ ret = -1;
+ }
+
+freeData:
+ free(data);
+ return ret;
+}
+
+static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve FW activate history table.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ 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));
+ ret = -1;
+ goto out;
+ }
+
+ /* Get the 0xC0 log data */
+ struct nvme_get_log_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .lid = WDC_NVME_GET_SMART_CLOUD_ATTR_LOG_ID,
+ .nsid = 0xFFFFFFFF,
+ .lpo = 0,
+ .lsp = NVME_LOG_LSP_NONE,
+ .lsi = 0,
+ .rae = false,
+ .uuidx = uuid_index,
+ .csi = NVME_CSI_NVM,
+ .ot = false,
+ .len = WDC_NVME_SMART_CLOUD_ATTR_LEN,
+ .log = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ ret = nvme_get_log(&args);
+
+ 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(r, dev,
+ cfg.output_format);
+ }
+ else {
+ ret = wdc_get_fw_act_history(r, dev,
+ cfg.output_format);
+ }
+ } else {
+ ret = wdc_get_fw_act_history_C2(r, dev, cfg.output_format);
+ }
+
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret);
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ 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, NULL);
+ nvme_show_status(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_features_simple(fd, WDC_NVME_CLEAR_FW_ACT_HIST_VU_FID, 0, value,
+ false, &result);
+
+ nvme_show_status(ret);
+ return ret;
+}
+
+static int wdc_clear_fw_activate_history(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Clear FW activate history table.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ 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;
+ }
+
+ if (capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY)
+ ret = wdc_do_clear_fw_activate_history_vuc(dev_fd(dev));
+ else
+ ret = wdc_do_clear_fw_activate_history_fid(dev_fd(dev));
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_vs_telemetry_controller_option(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Disable/Enable Controller Option of the Telemetry Log Page.";
+ char *disable = "Disable controller option of the telemetry log page.";
+ char *enable = "Enable controller option of the telemetry log page.";
+ char *status = "Displays the current state of the controller initiated log page.";
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ __u32 result;
+ int ret = -1;
+
+
+ struct config {
+ bool disable;
+ bool enable;
+ bool status;
+ };
+
+ struct config cfg = {
+ .disable = false,
+ .enable = false,
+ .status = false,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("disable", 'd', &cfg.disable, disable),
+ OPT_FLAG("enable", 'e', &cfg.enable, enable),
+ OPT_FLAG("status", 's', &cfg.status, status),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG) != WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* allow only one option at a time */
+ if ((cfg.disable + cfg.enable + cfg.status) > 1) {
+
+ fprintf(stderr, "ERROR : WDC : Invalid option\n");
+ ret = -1;
+ goto out;
+ }
+
+ if (cfg.disable) {
+ ret = nvme_set_features_simple(dev_fd(dev),
+ WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID,
+ 0, 1, false, &result);
+
+ wdc_clear_reason_id(dev);
+ }
+ else {
+ if (cfg.enable) {
+ ret = nvme_set_features_simple(dev_fd(dev),
+ WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID,
+ 0, 0, false, &result);
+ }
+ else if (cfg.status) {
+ ret = nvme_get_features_simple(dev_fd(dev),
+ WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID,
+ 0, &result);
+ if (ret == 0) {
+ if (result)
+ fprintf(stderr, "Controller Option Telemetry Log Page State: Disabled\n");
+ else
+ fprintf(stderr, "Controller Option Telemetry Log Page State: Enabled\n");
+ } else {
+ nvme_show_status(ret);
+ }
+ }
+ else {
+ fprintf(stderr, "ERROR : WDC: unsupported option for this command\n");
+ fprintf(stderr, "Please provide an option, -d, -e or -s\n");
+ ret = -1;
+ goto out;
+ }
+
+ }
+
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+
+static int wdc_get_serial_and_fw_rev(struct nvme_dev *dev, char *sn, char *fw_rev)
+{
+ int i;
+ int ret;
+ struct nvme_id_ctrl ctrl;
+
+ i = sizeof (ctrl.sn) - 1;
+ memset(sn, 0, WDC_SERIAL_NO_LEN);
+ memset(fw_rev, 0, WDC_NVME_FIRMWARE_REV_LEN);
+ memset(&ctrl, 0, sizeof (struct nvme_id_ctrl));
+ ret = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed "
+ "0x%x\n", ret);
+ return -1;
+ }
+ /* Remove trailing spaces from the name */
+ while (i && ctrl.sn[i] == ' ') {
+ ctrl.sn[i] = '\0';
+ i--;
+ }
+ snprintf(sn, WDC_SERIAL_NO_LEN, "%s", ctrl.sn);
+ snprintf(fw_rev, WDC_NVME_FIRMWARE_REV_LEN, "%s", ctrl.fr);
+
+ return 0;
+}
+
+static int wdc_get_max_transfer_len(struct nvme_dev *dev, __u32 *maxTransferLen)
+{
+ int ret = 0;
+ struct nvme_id_ctrl ctrl;
+
+ __u32 maxTransferLenDevice = 0;
+
+ memset(&ctrl, 0, sizeof (struct nvme_id_ctrl));
+ ret = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed 0x%x\n", ret);
+ return -1;
+ }
+
+ maxTransferLenDevice = (1 << ctrl.mdts) * getpagesize();
+ *maxTransferLen = maxTransferLenDevice;
+
+ return ret;
+}
+
+static int wdc_de_VU_read_size(struct nvme_dev *dev, __u32 fileId,
+ __u16 spiDestn, __u32* logSize)
+{
+ int ret = WDC_STATUS_FAILURE;
+ struct nvme_passthru_cmd cmd;
+
+ if(!dev || !logSize )
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ memset(&cmd,0,sizeof(struct nvme_passthru_cmd));
+ cmd.opcode = WDC_DE_VU_READ_SIZE_OPCODE;
+ cmd.nsid = WDC_DE_DEFAULT_NAMESPACE_ID;
+ cmd.cdw13 = fileId<<16;
+ cmd.cdw14 = spiDestn;
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL);
+
+ if (!ret && logSize)
+ *logSize = cmd.result;
+ if( ret != WDC_STATUS_SUCCESS) {
+ fprintf(stderr, "ERROR : WDC : VUReadSize() failed, ");
+ nvme_show_status(ret);
+ }
+
+ end:
+ return ret;
+}
+
+static int wdc_de_VU_read_buffer(struct nvme_dev *dev, __u32 fileId,
+ __u16 spiDestn, __u32 offsetInDwords,
+ __u8* dataBuffer, __u32* bufferSize)
+{
+ int ret = WDC_STATUS_FAILURE;
+ struct nvme_passthru_cmd cmd;
+ __u32 noOfDwordExpected = 0;
+
+ if(!dev || !dataBuffer || !bufferSize)
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ memset(&cmd,0,sizeof(struct nvme_passthru_cmd));
+ noOfDwordExpected = *bufferSize/sizeof(__u32);
+ cmd.opcode = WDC_DE_VU_READ_BUFFER_OPCODE;
+ cmd.nsid = WDC_DE_DEFAULT_NAMESPACE_ID;
+ cmd.cdw10 = noOfDwordExpected;
+ cmd.cdw13 = fileId<<16;
+ cmd.cdw14 = spiDestn;
+ cmd.cdw15 = offsetInDwords;
+
+ cmd.addr = (__u64)(__u64)(uintptr_t)dataBuffer;
+ cmd.data_len = *bufferSize;
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &cmd, NULL);
+
+ if( ret != WDC_STATUS_SUCCESS) {
+ fprintf(stderr, "ERROR : WDC : VUReadBuffer() failed, ");
+ nvme_show_status(ret);
+ }
+
+ end:
+ return ret;
+}
+
+static int wdc_get_log_dir_max_entries(struct nvme_dev *dev, __u32* maxNumOfEntries)
+{
+ int ret = WDC_STATUS_FAILURE;
+ __u32 headerPayloadSize = 0;
+ __u8* fileIdOffsetsBuffer = NULL;
+ __u32 fileIdOffsetsBufferSize = 0;
+ __u32 fileNum = 0;
+ __u16 fileOffset = 0;
+
+
+ if (!dev || !maxNumOfEntries)
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ return ret;
+ }
+ /* 1.Get log directory first four bytes */
+ if (WDC_STATUS_SUCCESS != (ret = wdc_de_VU_read_size(dev, 0, 5, (__u32*)&headerPayloadSize)))
+ {
+ fprintf(stderr, "ERROR : WDC : %s: Failed to get headerPayloadSize from file directory 0x%x\n",
+ __func__, ret);
+ return ret;
+ }
+
+ fileIdOffsetsBufferSize = WDC_DE_FILE_HEADER_SIZE + (headerPayloadSize * WDC_DE_FILE_OFFSET_SIZE);
+ fileIdOffsetsBuffer = (__u8*)calloc(1, fileIdOffsetsBufferSize);
+
+ /* 2.Read to get file offsets */
+ if (WDC_STATUS_SUCCESS != (ret = wdc_de_VU_read_buffer(dev, 0, 5, 0, fileIdOffsetsBuffer, &fileIdOffsetsBufferSize)))
+ {
+ fprintf(stderr, "ERROR : WDC : %s: Failed to get fileIdOffsets from file directory 0x%x\n",
+ __func__, ret);
+ goto end;
+ }
+ /* 3.Determine valid entries */
+ for (fileNum = 0; fileNum < (headerPayloadSize - WDC_DE_FILE_HEADER_SIZE) / WDC_DE_FILE_OFFSET_SIZE; fileNum++)
+ {
+ fileOffset = (fileIdOffsetsBuffer[WDC_DE_FILE_HEADER_SIZE + (fileNum * WDC_DE_FILE_OFFSET_SIZE)] << 8) +
+ fileIdOffsetsBuffer[WDC_DE_FILE_HEADER_SIZE + (fileNum * WDC_DE_FILE_OFFSET_SIZE) + 1];
+ if (!fileOffset)
+ continue;
+ (*maxNumOfEntries)++;
+ }
+end:
+ free(fileIdOffsetsBuffer);
+ return ret;
+}
+
+static WDC_DRIVE_ESSENTIAL_TYPE wdc_get_essential_type(__u8 fileName[])
+{
+ WDC_DRIVE_ESSENTIAL_TYPE essentialType = WDC_DE_TYPE_NONE;
+
+ if (wdc_UtilsStrCompare((char*)fileName, WDC_DE_CORE_DUMP_FILE_NAME) == 0)
+ {
+ essentialType = WDC_DE_TYPE_DUMPSNAPSHOT;
+ }
+ else if (wdc_UtilsStrCompare((char*)fileName, WDC_DE_EVENT_LOG_FILE_NAME) == 0)
+ {
+ essentialType = WDC_DE_TYPE_EVENTLOG;
+ }
+ else if (wdc_UtilsStrCompare((char*)fileName, WDC_DE_MANUFACTURING_INFO_PAGE_FILE_NAME) == 0)
+ {
+ essentialType = WDC_DE_TYPE_NVME_MANF_INFO;
+ }
+
+ return essentialType;
+}
+
+static int wdc_fetch_log_directory(struct nvme_dev *dev, PWDC_DE_VU_LOG_DIRECTORY directory)
+{
+ int ret = WDC_STATUS_FAILURE;
+ __u8 *fileOffset = NULL;
+ __u8 *fileDirectory = NULL;
+ __u32 headerSize = 0;
+ __u32 fileNum = 0, startIdx = 0;
+ __u16 fileOffsetTemp = 0;
+ __u32 entryId = 0;
+ __u32 fileDirectorySize = 0;
+
+ if (!dev || !directory) {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ ret = wdc_de_VU_read_size(dev, 0, 5, &fileDirectorySize);
+ if (WDC_STATUS_SUCCESS != ret) {
+ fprintf(stderr,
+ "ERROR : WDC : %s: Failed to get filesystem directory size, ret = %d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ fileDirectory = (__u8*)calloc(1, fileDirectorySize);
+ ret = wdc_de_VU_read_buffer(dev, 0, 5, 0, fileDirectory, &fileDirectorySize);
+ if (WDC_STATUS_SUCCESS != ret) {
+ fprintf(stderr,
+ "ERROR : WDC : %s: Failed to get filesystem directory, ret = %d\n",
+ __func__, ret);
+ goto end;
+ }
+
+ /* First four bytes of header directory is headerSize */
+ memcpy(&headerSize, fileDirectory, WDC_DE_FILE_HEADER_SIZE);
+
+ /* minimum buffer for 1 entry is required */
+ if (directory->maxNumLogEntries == 0) {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ for (fileNum = 0;
+ fileNum < (headerSize - WDC_DE_FILE_HEADER_SIZE) / WDC_DE_FILE_OFFSET_SIZE;
+ fileNum++) {
+ if (entryId >= directory->maxNumLogEntries)
+ break;
+
+ startIdx = WDC_DE_FILE_HEADER_SIZE + (fileNum * WDC_DE_FILE_OFFSET_SIZE);
+ memcpy(&fileOffsetTemp, fileDirectory + startIdx, sizeof(fileOffsetTemp));
+ fileOffset = fileDirectory + fileOffsetTemp;
+
+ if (0 == fileOffsetTemp)
+ continue;
+
+ memset(&directory->logEntry[entryId], 0, sizeof(WDC_DRIVE_ESSENTIALS));
+ memcpy(&directory->logEntry[entryId].metaData, fileOffset, sizeof(WDC_DE_VU_FILE_META_DATA));
+ directory->logEntry[entryId].metaData.fileName[WDC_DE_FILE_NAME_SIZE - 1] = '\0';
+ wdc_UtilsDeleteCharFromString((char*)directory->logEntry[entryId].metaData.fileName,
+ WDC_DE_FILE_NAME_SIZE, ' ');
+ if (0 == directory->logEntry[entryId].metaData.fileID)
+ continue;
+
+ directory->logEntry[entryId].essentialType = wdc_get_essential_type(directory->logEntry[entryId].metaData.fileName);
+ /*fprintf(stderr, "WDC : %s: NVMe VU Log Entry %d, fileName = %s, fileSize = 0x%lx, fileId = 0x%x\n",
+ __func__, entryId, directory->logEntry[entryId].metaData.fileName,
+ (long unsigned int)directory->logEntry[entryId].metaData.fileSize, directory->logEntry[entryId].metaData.fileID);
+ */
+ entryId++;
+ }
+
+ directory->numOfValidLogEntries = entryId;
+end:
+ if (fileDirectory != NULL)
+ free(fileDirectory);
+ return ret;
+}
+
+static int wdc_fetch_log_file_from_device(struct nvme_dev *dev, __u32 fileId,
+ __u16 spiDestn, __u64 fileSize, __u8* dataBuffer)
+{
+ int ret = WDC_STATUS_FAILURE;
+ __u32 chunckSize = WDC_DE_VU_READ_BUFFER_STANDARD_OFFSET;
+ __u32 maximumTransferLength = 0;
+ __u32 buffSize = 0;
+ __u64 offsetIdx = 0;
+
+ if (!dev || !dataBuffer || !fileSize)
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ if (wdc_get_max_transfer_len(dev, &maximumTransferLength) < 0) {
+ ret = WDC_STATUS_FAILURE;
+ goto end;
+ }
+
+ /* Fetch Log File Data */
+ if ((fileSize >= maximumTransferLength) || (fileSize > 0xFFFFFFFF))
+ {
+ chunckSize = WDC_DE_VU_READ_BUFFER_STANDARD_OFFSET;
+ if (maximumTransferLength < WDC_DE_VU_READ_BUFFER_STANDARD_OFFSET)
+ chunckSize = maximumTransferLength;
+
+ buffSize = chunckSize;
+ for (offsetIdx = 0; (offsetIdx * chunckSize) < fileSize; offsetIdx++)
+ {
+ if (((offsetIdx * chunckSize) + buffSize) > fileSize)
+ buffSize = (__u32)(fileSize - (offsetIdx * chunckSize));
+ /* Limitation in VU read buffer - offsetIdx and bufferSize are not greater than u32 */
+ ret = wdc_de_VU_read_buffer(dev, fileId, spiDestn,
+ (__u32)((offsetIdx * chunckSize) / sizeof(__u32)), dataBuffer + (offsetIdx * chunckSize), &buffSize);
+ if (ret != WDC_STATUS_SUCCESS)
+ {
+ fprintf(stderr, "ERROR : WDC : %s: wdc_de_VU_read_buffer failed with ret = %d, fileId = 0x%x, fileSize = 0x%lx\n",
+ __func__, ret, fileId, (long unsigned int)fileSize);
+ break;
+ }
+ }
+ } else {
+ buffSize = (__u32)fileSize;
+ ret = wdc_de_VU_read_buffer(dev, fileId, spiDestn,
+ (__u32)((offsetIdx * chunckSize) / sizeof(__u32)), dataBuffer, &buffSize);
+ if (ret != WDC_STATUS_SUCCESS)
+ {
+ fprintf(stderr, "ERROR : WDC : %s: wdc_de_VU_read_buffer failed with ret = %d, fileId = 0x%x, fileSize = 0x%lx\n",
+ __func__, ret, fileId, (long unsigned int)fileSize);
+ }
+ }
+
+ end:
+ return ret;
+}
+
+static int wdc_de_get_dump_trace(struct nvme_dev *dev, char * filePath, __u16 binFileNameLen, char *binFileName)
+{
+ int ret = WDC_STATUS_FAILURE;
+ __u8 *readBuffer = NULL;
+ __u32 readBufferLen = 0;
+ __u32 lastPktReadBufferLen = 0;
+ __u32 maxTransferLen = 0;
+ __u32 dumptraceSize = 0;
+ __u32 chunkSize = 0;
+ __u32 chunks = 0;
+ __u32 offset = 0;
+ __u8 loop = 0;
+ __u16 i = 0;
+ __u32 maximumTransferLength = 0;
+
+ if (!dev || !binFileName || !filePath)
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ return ret;
+ }
+
+ if (wdc_get_max_transfer_len(dev, &maximumTransferLength) < 0)
+ return WDC_STATUS_FAILURE;
+
+ do
+ {
+ /* Get dumptrace size */
+ ret = wdc_de_VU_read_size(dev, 0, WDC_DE_DUMPTRACE_DESTINATION, &dumptraceSize);
+ if (ret != WDC_STATUS_SUCCESS)
+ {
+ fprintf(stderr, "ERROR : WDC : %s: wdc_de_VU_read_size failed with ret = %d\n",
+ __func__, ret);
+ break;
+ }
+
+ /* Make sure the size requested is greater than dword */
+ if (dumptraceSize < 4)
+ {
+ ret = WDC_STATUS_FAILURE;
+ fprintf(stderr, "ERROR : WDC : %s: wdc_de_VU_read_size failed, read size is less than 4 bytes, dumptraceSize = 0x%x\n",
+ __func__, dumptraceSize);
+ break;
+ }
+
+ /* Choose the least max transfer length */
+ maxTransferLen = maximumTransferLength < WDC_DE_READ_MAX_TRANSFER_SIZE ? maximumTransferLength : WDC_DE_READ_MAX_TRANSFER_SIZE;
+
+ /* Comment from FW Team:
+ * The max non - block transfer size is 0xFFFF (16 bits allowed as the block size).Use 0x8000
+ * to keep it on a word - boundary.
+ * max_xfer = int(pow(2, id_data['MDTS'])) * 4096 # 4k page size as reported in pcie capabiltiies
+ */
+ chunkSize = dumptraceSize < maxTransferLen ? dumptraceSize : maxTransferLen;
+ chunks = (dumptraceSize / maxTransferLen) + ((dumptraceSize % maxTransferLen) ? 1 : 0);
+
+ readBuffer = (unsigned char *)calloc(dumptraceSize, sizeof(unsigned char));
+ readBufferLen = chunkSize;
+ lastPktReadBufferLen = (dumptraceSize % maxTransferLen) ? (dumptraceSize % maxTransferLen) : chunkSize;
+
+ if (readBuffer == NULL)
+ {
+ fprintf(stderr, "ERROR : WDC : %s: readBuffer calloc failed\n", __func__);
+ ret = WDC_STATUS_INSUFFICIENT_MEMORY;
+ break;
+ }
+
+ for (i = 0; i < chunks; i++)
+ {
+ offset = ((i*chunkSize) / 4);
+
+ /* Last loop call, Assign readBufferLen to read only left over bytes */
+ if (i == (chunks - 1))
+ {
+ readBufferLen = lastPktReadBufferLen;
+ }
+
+ ret = wdc_de_VU_read_buffer(dev, 0, WDC_DE_DUMPTRACE_DESTINATION, 0, readBuffer + offset, &readBufferLen);
+ if (ret != WDC_STATUS_SUCCESS)
+ {
+ fprintf(stderr, "ERROR : WDC : %s: wdc_de_VU_read_buffer failed, ret = %d on offset 0x%x\n",
+ __func__, ret, offset);
+ break;
+ }
+ }
+ } while (loop);
+
+ if (ret == WDC_STATUS_SUCCESS)
+ {
+ ret = wdc_WriteToFile(binFileName, (char*)readBuffer, dumptraceSize);
+ if (ret != WDC_STATUS_SUCCESS)
+ fprintf(stderr, "ERROR : WDC : %s: wdc_WriteToFile failed, ret = %d\n", __func__, ret);
+ } else {
+ fprintf(stderr, "ERROR : WDC : %s: Read Buffer Loop failed, ret = %d\n", __func__, ret);
+ }
+
+ if (readBuffer)
+ {
+ free(readBuffer);
+ }
+
+ return ret;
+}
+
+static int wdc_do_drive_essentials(nvme_root_t r, struct nvme_dev *dev,
+ char *dir, char *key)
+{
+ int ret = 0;
+ void *retPtr;
+ char fileName[MAX_PATH_LEN];
+ __s8 bufferFolderPath[MAX_PATH_LEN];
+ char bufferFolderName[MAX_PATH_LEN];
+ char tarFileName[MAX_PATH_LEN];
+ char tarFiles[MAX_PATH_LEN];
+ char tarCmd[MAX_PATH_LEN+MAX_PATH_LEN];
+ UtilsTimeInfo timeInfo;
+ __u8 timeString[MAX_PATH_LEN];
+ __u8 serialNo[WDC_SERIAL_NO_LEN];
+ __u8 firmwareRevision[WDC_NVME_FIRMWARE_REV_LEN];
+ __u8 idSerialNo[WDC_SERIAL_NO_LEN];
+ __u8 idFwRev[WDC_NVME_FIRMWARE_REV_LEN];
+ __u8 featureIdBuff[4];
+ char currDir[MAX_PATH_LEN];
+ char *dataBuffer = NULL;
+ __u32 elogNumEntries, elogBufferSize;
+ __u32 dataBufferSize;
+ __u32 listIdx = 0;
+ __u32 vuLogIdx = 0;
+ __u32 result;
+ __u32 maxNumOfVUFiles = 0;
+ struct nvme_id_ctrl ctrl;
+ struct nvme_id_ns ns;
+ struct nvme_error_log_page *elogBuffer;
+ struct nvme_smart_log smart_log;
+ struct nvme_firmware_slot fw_log;
+ PWDC_NVME_DE_VU_LOGPAGES vuLogInput = NULL;
+ WDC_DE_VU_LOG_DIRECTORY deEssentialsList;
+
+ memset(bufferFolderPath,0,sizeof(bufferFolderPath));
+ memset(bufferFolderName,0,sizeof(bufferFolderName));
+ memset(tarFileName,0,sizeof(tarFileName));
+ memset(tarFiles,0,sizeof(tarFiles));
+ memset(tarCmd,0,sizeof(tarCmd));
+ memset(&timeInfo,0,sizeof(timeInfo));
+
+ if (wdc_get_serial_and_fw_rev(dev, (char *)idSerialNo, (char *)idFwRev))
+ {
+ fprintf(stderr, "ERROR : WDC : get serial # and fw revision failed\n");
+ return -1;
+ } else {
+ fprintf(stderr, "Get Drive Essentials Data for device serial #: %s and fw revision: %s\n",
+ idSerialNo, idFwRev);
+ }
+
+ /* Create Drive Essentials directory */
+ wdc_UtilsGetTime(&timeInfo);
+ memset(timeString, 0, sizeof(timeString));
+ wdc_UtilsSnprintf((char*)timeString, MAX_PATH_LEN, "%02u%02u%02u_%02u%02u%02u",
+ timeInfo.year, timeInfo.month, timeInfo.dayOfMonth,
+ timeInfo.hour, timeInfo.minute, timeInfo.second);
+
+ wdc_UtilsSnprintf((char*)serialNo,WDC_SERIAL_NO_LEN,(char*)idSerialNo);
+ /* Remove any space form serialNo */
+ wdc_UtilsDeleteCharFromString((char*)serialNo, WDC_SERIAL_NO_LEN, ' ');
+
+ memset(firmwareRevision, 0, sizeof(firmwareRevision));
+ wdc_UtilsSnprintf((char*)firmwareRevision, WDC_NVME_FIRMWARE_REV_LEN, (char*)idFwRev);
+ /* Remove any space form FirmwareRevision */
+ wdc_UtilsDeleteCharFromString((char*)firmwareRevision, WDC_NVME_FIRMWARE_REV_LEN, ' ');
+
+ wdc_UtilsSnprintf((char*)bufferFolderName, MAX_PATH_LEN, "%s_%s_%s_%s",
+ "DRIVE_ESSENTIALS", (char*)serialNo, (char*)firmwareRevision, (char*)timeString);
+
+ if (dir != NULL) {
+ wdc_UtilsSnprintf((char*)bufferFolderPath, MAX_PATH_LEN, "%s%s%s",
+ (char *)dir, WDC_DE_PATH_SEPARATOR, (char *)bufferFolderName);
+ } else {
+ retPtr = getcwd((char*)currDir, MAX_PATH_LEN);
+ if (retPtr != NULL)
+ wdc_UtilsSnprintf((char*)bufferFolderPath, MAX_PATH_LEN, "%s%s%s",
+ (char *)currDir, WDC_DE_PATH_SEPARATOR, (char *)bufferFolderName);
+ else {
+ fprintf(stderr, "ERROR : WDC : get current working directory failed\n");
+ return -1;
+ }
+ }
+
+ ret = wdc_UtilsCreateDir((char*)bufferFolderPath);
+ if (ret != 0)
+ {
+ fprintf(stderr, "ERROR : WDC : create directory failed, ret = %d, dir = %s\n", ret, bufferFolderPath);
+ return -1;
+ } else {
+ fprintf(stderr, "Store Drive Essentials bin files in directory: %s\n", bufferFolderPath);
+ }
+
+ /* Get Identify Controller Data */
+ memset(&ctrl, 0, sizeof (struct nvme_id_ctrl));
+ ret = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed, ret = %d\n", ret);
+ return -1;
+ } else {
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "IdentifyController", (char*)serialNo, (char*)timeString);
+ wdc_WriteToFile(fileName, (char*)&ctrl, sizeof (struct nvme_id_ctrl));
+ }
+
+ memset(&ns, 0, sizeof (struct nvme_id_ns));
+ ret = nvme_identify_ns(dev_fd(dev), 1, &ns);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_identify_ns() failed, ret = %d\n", ret);
+ } else {
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "IdentifyNamespace", (char*)serialNo, (char*)timeString);
+ wdc_WriteToFile(fileName, (char*)&ns, sizeof (struct nvme_id_ns));
+ }
+
+ /* Get Log Pages (0x01, 0x02, 0x03, 0xC0 and 0xE3) */
+ elogNumEntries = WDC_DE_DEFAULT_NUMBER_OF_ERROR_ENTRIES;
+ elogBufferSize = elogNumEntries*sizeof(struct nvme_error_log_page);
+ dataBuffer = calloc(1, elogBufferSize);
+ elogBuffer = (struct nvme_error_log_page *)dataBuffer;
+
+ ret = nvme_get_log_error(dev_fd(dev), elogNumEntries, false,
+ elogBuffer);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_error_log() failed, ret = %d\n", ret);
+ } else {
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "ErrorLog", (char*)serialNo, (char*)timeString);
+ wdc_WriteToFile(fileName, (char*)elogBuffer, elogBufferSize);
+ }
+
+ free(dataBuffer);
+ dataBuffer = NULL;
+
+ /* Get Smart log page */
+ memset(&smart_log, 0, sizeof (struct nvme_smart_log));
+ ret = nvme_get_log_smart(dev_fd(dev), NVME_NSID_ALL, false,
+ &smart_log);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_smart_log() failed, ret = %d\n", ret);
+ } else {
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "SmartLog", (char*)serialNo, (char*)timeString);
+ wdc_WriteToFile(fileName, (char*)&smart_log, sizeof(struct nvme_smart_log));
+ }
+
+ /* Get FW Slot log page */
+ memset(&fw_log, 0, sizeof (struct nvme_firmware_slot));
+ ret = nvme_get_log_fw_slot(dev_fd(dev), false, &fw_log);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_fw_log() failed, ret = %d\n", ret);
+ } else {
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "FwSLotLog", (char*)serialNo, (char*)timeString);
+ wdc_WriteToFile(fileName, (char*)&fw_log, sizeof(struct nvme_firmware_slot));
+ }
+
+ /* Get VU log pages */
+ /* define inputs for vendor unique log pages */
+ vuLogInput = (PWDC_NVME_DE_VU_LOGPAGES)calloc(1, sizeof(WDC_NVME_DE_VU_LOGPAGES));
+ vuLogInput->numOfVULogPages = sizeof(deVULogPagesList) / sizeof(deVULogPagesList[0]);
+
+ for (vuLogIdx = 0; vuLogIdx < vuLogInput->numOfVULogPages; vuLogIdx++)
+ {
+ dataBufferSize = deVULogPagesList[vuLogIdx].logPageLen;
+ dataBuffer = calloc(1, dataBufferSize);
+ memset(dataBuffer, 0, dataBufferSize);
+
+ ret = nvme_get_log_simple(dev_fd(dev),
+ deVULogPagesList[vuLogIdx].logPageId,
+ dataBufferSize, dataBuffer);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_get_log() for log page 0x%x failed, ret = %d\n",
+ deVULogPagesList[vuLogIdx].logPageId, ret);
+ } else {
+ wdc_UtilsDeleteCharFromString((char*)deVULogPagesList[vuLogIdx].logPageIdStr, 4, ' ');
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "LogPage", (char*)&deVULogPagesList[vuLogIdx].logPageIdStr, (char*)serialNo, (char*)timeString);
+ wdc_WriteToFile(fileName, (char*)dataBuffer, dataBufferSize);
+ }
+
+ free(dataBuffer);
+ dataBuffer = NULL;
+ }
+
+ free(vuLogInput);
+
+ /* Get NVMe Features (0x01, 0x02, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C) */
+ for (listIdx = 1; listIdx < (sizeof(deFeatureIdList) / sizeof(deFeatureIdList[0])); listIdx++)
+ {
+ memset(featureIdBuff, 0, sizeof(featureIdBuff));
+ /* skipping LbaRangeType as it is an optional nvme command and not supported */
+ if (deFeatureIdList[listIdx].featureId == FID_LBA_RANGE_TYPE)
+ continue;
+ ret = nvme_get_features_data(dev_fd(dev),
+ (enum nvme_features_id)deFeatureIdList[listIdx].featureId,
+ WDC_DE_GLOBAL_NSID,
+ sizeof(featureIdBuff),
+ &featureIdBuff, &result);
+
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_get_feature id 0x%x failed, ret = %d\n",
+ deFeatureIdList[listIdx].featureId, ret);
+ } else {
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s0x%x_%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ "FEATURE_ID_", deFeatureIdList[listIdx].featureId,
+ deFeatureIdList[listIdx].featureName, serialNo, timeString);
+ wdc_WriteToFile(fileName, (char*)featureIdBuff, sizeof(featureIdBuff));
+ }
+ }
+
+ /* Read Debug Directory */
+ ret = wdc_get_log_dir_max_entries(dev, &maxNumOfVUFiles);
+ if (ret == WDC_STATUS_SUCCESS)
+ {
+ memset(&deEssentialsList, 0, sizeof(deEssentialsList));
+ deEssentialsList.logEntry = (WDC_DRIVE_ESSENTIALS*)calloc(1, sizeof(WDC_DRIVE_ESSENTIALS)*maxNumOfVUFiles);
+ deEssentialsList.maxNumLogEntries = maxNumOfVUFiles;
+
+ /* Fetch VU File Directory */
+ ret = wdc_fetch_log_directory(dev, &deEssentialsList);
+ if (ret == WDC_STATUS_SUCCESS)
+ {
+ /* Get Debug Data Files */
+ for (listIdx = 0; listIdx < deEssentialsList.numOfValidLogEntries; listIdx++)
+ {
+ if (0 == deEssentialsList.logEntry[listIdx].metaData.fileSize)
+ {
+ fprintf(stderr, "ERROR : WDC : File Size for %s is 0\n",
+ deEssentialsList.logEntry[listIdx].metaData.fileName);
+ ret = WDC_STATUS_FILE_SIZE_ZERO;
+ } else {
+ /* Fetch Log File Data */
+ dataBuffer = (char *)calloc(1, (size_t)deEssentialsList.logEntry[listIdx].metaData.fileSize);
+ ret = wdc_fetch_log_file_from_device(dev, deEssentialsList.logEntry[listIdx].metaData.fileID, WDC_DE_DESTN_SPI, deEssentialsList.logEntry[listIdx].metaData.fileSize,
+ (__u8 *)dataBuffer);
+
+ /* Write databuffer to file */
+ if (ret == WDC_STATUS_SUCCESS)
+ {
+ memset(fileName, 0, sizeof(fileName));
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s.bin", bufferFolderPath, WDC_DE_PATH_SEPARATOR,
+ deEssentialsList.logEntry[listIdx].metaData.fileName, serialNo, timeString);
+ if (deEssentialsList.logEntry[listIdx].metaData.fileSize > 0xFFFFFFFF)
+ {
+ wdc_WriteToFile(fileName, dataBuffer, 0xFFFFFFFF);
+ wdc_WriteToFile(fileName, dataBuffer + 0xFFFFFFFF, (__u32)(deEssentialsList.logEntry[listIdx].metaData.fileSize - 0xFFFFFFFF));
+ } else {
+ wdc_WriteToFile(fileName, dataBuffer, (__u32)deEssentialsList.logEntry[listIdx].metaData.fileSize);
+ }
+ } else {
+ fprintf(stderr, "ERROR : WDC : wdc_fetch_log_file_from_device: %s failed, ret = %d\n",
+ deEssentialsList.logEntry[listIdx].metaData.fileName, ret);
+ }
+ free(dataBuffer);
+ dataBuffer = NULL;
+ }
+ }
+ } else {
+ fprintf(stderr, "WDC : wdc_fetch_log_directory failed, ret = %d\n", ret);
+ }
+
+ free(deEssentialsList.logEntry);
+ deEssentialsList.logEntry = NULL;
+ } else {
+ fprintf(stderr, "WDC : wdc_get_log_dir_max_entries failed, ret = %d\n", ret);
+ }
+
+ /* Get Dump Trace Data */
+ wdc_UtilsSnprintf(fileName, MAX_PATH_LEN, "%s%s%s_%s_%s.bin", (char*)bufferFolderPath, WDC_DE_PATH_SEPARATOR, "dumptrace", serialNo, timeString);
+ if (WDC_STATUS_SUCCESS != (ret = wdc_de_get_dump_trace(dev, (char*)bufferFolderPath, 0, fileName)))
+ {
+ fprintf(stderr, "ERROR : WDC : wdc_de_get_dump_trace failed, ret = %d\n", ret);
+ }
+
+ /* Tar the Drive Essentials directory */
+ wdc_UtilsSnprintf(tarFileName, sizeof(tarFileName), "%s%s", (char*)bufferFolderPath, WDC_DE_TAR_FILE_EXTN);
+ if (dir != NULL) {
+ wdc_UtilsSnprintf(tarFiles, sizeof(tarFiles), "%s%s%s%s%s",
+ (char*)dir, WDC_DE_PATH_SEPARATOR, (char*)bufferFolderName, WDC_DE_PATH_SEPARATOR, WDC_DE_TAR_FILES);
+ } else {
+ wdc_UtilsSnprintf(tarFiles, sizeof(tarFiles), "%s%s%s", (char*)bufferFolderName, WDC_DE_PATH_SEPARATOR, WDC_DE_TAR_FILES);
+ }
+ wdc_UtilsSnprintf(tarCmd, sizeof(tarCmd), "%s %s %s", WDC_DE_TAR_CMD, (char*)tarFileName, (char*)tarFiles);
+
+ ret = system(tarCmd);
+
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : Tar of Drive Essentials data failed, ret = %d\n", ret);
+ }
+
+ fprintf(stderr, "Get of Drive Essentials data successful\n");
+ nvme_free_tree(r);
+ return 0;
+}
+
+static int wdc_drive_essentials(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Capture Drive Essentials.";
+ char *dirName = "Output directory pathname.";
+ char d[PATH_MAX] = {0};
+ char k[PATH_MAX] = {0};
+ __u64 capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ char *d_ptr;
+ int ret;
+
+ struct config {
+ char *dirName;
+ };
+
+ struct config cfg = {
+ .dirName = NULL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.dirName, dirName),
+ OPT_END()
+ };
+
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_DRIVE_ESSENTIALS) != WDC_DRIVE_CAP_DRIVE_ESSENTIALS) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ if (cfg.dirName != NULL) {
+ strncpy(d, cfg.dirName, PATH_MAX - 1);
+ d_ptr = d;
+ } else {
+ d_ptr = NULL;
+ }
+
+ ret = wdc_do_drive_essentials(r, dev, d_ptr, k);
+out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_do_drive_resize(struct nvme_dev *dev, uint64_t new_size)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = WDC_NVME_DRIVE_RESIZE_OPCODE;
+ admin_cmd.cdw12 = ((WDC_NVME_DRIVE_RESIZE_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_DRIVE_RESIZE_CMD);
+ admin_cmd.cdw13 = new_size;
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+ return ret;
+}
+
+static int wdc_do_namespace_resize(struct nvme_dev *dev, __u32 nsid, __u32 op_option)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = WDC_NVME_NAMESPACE_RESIZE_OPCODE;
+ admin_cmd.nsid = nsid;
+ admin_cmd.cdw10 = op_option;
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+ return ret;
+}
+
+static int wdc_do_drive_info(struct nvme_dev *dev, __u32 *result)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_cmd));
+ admin_cmd.opcode = WDC_NVME_DRIVE_INFO_OPCODE;
+ admin_cmd.cdw12 = ((WDC_NVME_DRIVE_INFO_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_DRIVE_INFO_CMD);
+
+ ret = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL);
+
+ if (!ret && result)
+ *result = admin_cmd.result;
+
+ return ret;
+}
+
+static int wdc_drive_resize(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Send a Resize command.";
+ const char *size = "The new size (in GB) to resize the drive to.";
+ uint64_t capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ struct config {
+ uint64_t size;
+ };
+
+ struct config cfg = {
+ .size = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("size", 's', &cfg.size, size),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ wdc_check_device(r, dev);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_RESIZE) == WDC_DRIVE_CAP_RESIZE) {
+ ret = wdc_do_drive_resize(dev, cfg.size);
+ } else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ }
+
+ if (!ret)
+ printf("New size: %" PRIu64 " GB\n", cfg.size);
+
+ nvme_show_status(ret);
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_namespace_resize(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Send a Namespace Resize command.";
+ const char *namespace_id = "The namespace id to resize.";
+ const char *op_option = "The over provisioning option to set for namespace.";
+ uint64_t capabilities = 0;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 op_option;
+ };
+
+ struct config cfg = {
+ .namespace_id = 0x1,
+ .op_option = 0xF,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_UINT("op-option", 'o', &cfg.op_option, op_option),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ if ((cfg.op_option != 0x1) &&
+ (cfg.op_option != 0x2) &&
+ (cfg.op_option != 0x3) &&
+ (cfg.op_option != 0xF))
+ {
+ fprintf(stderr, "ERROR : WDC: unsupported OP option parameter\n");
+ dev_close(dev);
+ return -1;
+ }
+
+ r = nvme_scan(NULL);
+ wdc_check_device(r, dev);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_NS_RESIZE) == WDC_DRIVE_CAP_NS_RESIZE) {
+ ret = wdc_do_namespace_resize(dev, cfg.namespace_id,
+ cfg.op_option);
+
+ if (ret != 0)
+ printf("ERROR : WDC: Namespace Resize of namespace id 0x%x, op option 0x%x failed\n", cfg.namespace_id, cfg.op_option);
+ } else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ }
+
+ nvme_show_status(ret);
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_reason_identifier(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Retrieve telemetry log reason identifier.";
+ const char *log_id = "Log ID to retrieve - host - 7 or controller - 8";
+ const char *fname = "File name to save raw binary identifier";
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+ uint64_t capabilities = 0;
+ char f[PATH_MAX] = {0};
+ char fileSuffix[PATH_MAX] = {0};
+ UtilsTimeInfo timeInfo;
+ __u8 timeStamp[MAX_PATH_LEN];
+
+
+ struct config {
+ int log_id;
+ char *file;
+ };
+ struct config cfg = {
+ .log_id = 7,
+ .file = NULL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("log-id", 'i', &cfg.log_id, log_id),
+ OPT_FILE("file", 'o', &cfg.file, fname),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+
+ if (cfg.log_id != NVME_LOG_LID_TELEMETRY_HOST&& cfg.log_id != NVME_LOG_LID_TELEMETRY_CTRL) {
+ fprintf(stderr, "ERROR : WDC: Invalid Log ID. It must be 7 (Host) or 8 (Controller)\n");
+ ret = -1;
+ goto close_fd;
+ }
+
+ if (cfg.file != NULL) {
+ int verify_file;
+
+ /* verify the passed in file name and path is valid before getting the dump data */
+ verify_file = open(cfg.file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (verify_file < 0) {
+ fprintf(stderr, "ERROR : WDC: open : %s\n", strerror(errno));
+ ret = -1;
+ goto close_fd;
+ }
+ close(verify_file);
+ strncpy(f, cfg.file, PATH_MAX - 1);
+ } else {
+ wdc_UtilsGetTime(&timeInfo);
+ memset(timeStamp, 0, sizeof(timeStamp));
+ wdc_UtilsSnprintf((char*)timeStamp, MAX_PATH_LEN, "%02u%02u%02u_%02u%02u%02u",
+ timeInfo.year, timeInfo.month, timeInfo.dayOfMonth,
+ timeInfo.hour, timeInfo.minute, timeInfo.second);
+ if (cfg.log_id == NVME_LOG_LID_TELEMETRY_CTRL)
+ snprintf(fileSuffix, PATH_MAX, "_error_reason_identifier_ctlr_%s", (char*)timeStamp);
+ else
+ snprintf(fileSuffix, PATH_MAX, "_error_reason_identifier_host_%s", (char*)timeStamp);
+
+ if (wdc_get_serial_name(dev, f, PATH_MAX, fileSuffix) == -1) {
+ fprintf(stderr, "ERROR : WDC: failed to generate file name\n");
+ ret = -1;
+ goto close_fd;
+ }
+ if (strlen(f) > PATH_MAX - 5) {
+ fprintf(stderr, "ERROR : WDC: file name overflow\n");
+ ret = -1;
+ goto close_fd;
+ }
+ strcat(f, ".bin");
+ }
+
+ fprintf(stderr, "%s: filename = %s\n", __func__, f);
+
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_REASON_ID) == WDC_DRIVE_CAP_REASON_ID) {
+ ret = wdc_do_get_reason_id(dev, f, cfg.log_id);
+ } else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ }
+
+ nvme_show_status(ret);
+
+ close_fd:
+ dev_close(dev);
+ nvme_free_tree(r);
+ return ret;
+}
+
+static const char *nvme_log_id_to_string(__u8 log_id)
+{
+ switch (log_id) {
+ case NVME_LOG_LID_ERROR: return "Error Information Log ID";
+ case NVME_LOG_LID_SMART: return "Smart/Health Information Log ID";
+ case NVME_LOG_LID_FW_SLOT: return "Firmware Slot Information Log ID";
+ case NVME_LOG_LID_CHANGED_NS: return "Namespace Changed Log ID";
+ case NVME_LOG_LID_CMD_EFFECTS: return "Commamds Supported and Effects Log ID";
+ case NVME_LOG_LID_DEVICE_SELF_TEST: return "Device Self Test Log ID";
+ case NVME_LOG_LID_TELEMETRY_HOST: return "Telemetry Host Initiated Log ID";
+ case NVME_LOG_LID_TELEMETRY_CTRL: return "Telemetry Controller Generated Log ID";
+ case NVME_LOG_LID_ENDURANCE_GROUP: return "Endurance Group Log ID";
+ case NVME_LOG_LID_ANA: return "ANA Log ID";
+ case NVME_LOG_LID_PERSISTENT_EVENT: return "Persistent Event Log ID";
+ case NVME_LOG_LID_DISCOVER: return "Discovery Log ID";
+ case NVME_LOG_LID_RESERVATION: return "Reservation Notification Log ID";
+ case NVME_LOG_LID_SANITIZE: return "Sanitize Status Log ID";
+
+ case WDC_LOG_ID_C0: return "WDC Vendor Unique Log ID C0";
+ case WDC_LOG_ID_C1: return "WDC Vendor Unique Log ID C1";
+ case WDC_LOG_ID_C2: return "WDC Vendor Unique Log ID C2";
+ case WDC_LOG_ID_C3: return "WDC Vendor Unique Log ID C3";
+ case WDC_LOG_ID_C4: return "WDC Vendor Unique Log ID C4";
+ case WDC_LOG_ID_C5: return "WDC Vendor Unique Log ID C5";
+ case WDC_LOG_ID_C6: return "WDC Vendor Unique Log ID C6";
+ case WDC_LOG_ID_C8: return "WDC Vendor Unique Log ID C8";
+ case WDC_LOG_ID_CA: return "WDC Vendor Unique Log ID CA";
+ case WDC_LOG_ID_CB: return "WDC Vendor Unique Log ID CB";
+ case WDC_LOG_ID_D0: return "WDC Vendor Unique Log ID D0";
+ case WDC_LOG_ID_D1: return "WDC Vendor Unique Log ID D1";
+ case WDC_LOG_ID_D6: return "WDC Vendor Unique Log ID D6";
+ case WDC_LOG_ID_D7: return "WDC Vendor Unique Log ID D7";
+ case WDC_LOG_ID_D8: return "WDC Vendor Unique Log ID D8";
+ case WDC_LOG_ID_DE: return "WDC Vendor Unique Log ID DE";
+ case WDC_LOG_ID_F0: return "WDC Vendor Unique Log ID F0";
+ case WDC_LOG_ID_F1: return "WDC Vendor Unique Log ID F1";
+ case WDC_LOG_ID_F2: return "WDC Vendor Unique Log ID F2";
+ case WDC_LOG_ID_FA: return "WDC Vendor Unique Log ID FA";
+
+ default: return "Unknown Log ID";
+ }
+}
+
+static int wdc_log_page_directory(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve Log Page Directory.";
+ struct nvme_dev *dev;
+ int ret = 0;
+ nvme_root_t r;
+ __u64 capabilities = 0;
+ struct wdc_c2_cbs_data *cbs_data = NULL;
+ int i;
+ __u8 log_id = 0;
+ __u32 device_id, read_vendor_id;
+
+ 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|binary"),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ ret = validate_output_format(cfg.output_format);
+ if (ret < 0) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid output format\n", __func__);
+ dev_close(dev);
+ return ret;
+ }
+ ret = 0;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_LOG_PAGE_DIR) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ ret = wdc_get_pci_ids(r, dev, &device_id, &read_vendor_id);
+ log_id = (device_id == WDC_NVME_ZN350_DEV_ID || device_id == WDC_NVME_ZN350_DEV_ID_1) ?
+ WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE_C8 : WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE;
+ /* verify the 0xC2 Device Manageability log page is supported */
+ if (wdc_nvme_check_supported_log_page(r, dev, log_id) == false) {
+ fprintf(stderr, "%s: ERROR : WDC : 0x%x Log Page not supported\n", __func__, log_id);
+ ret = -1;
+ goto out;
+ }
+
+ if (get_dev_mgment_cbs_data(r, dev, WDC_C2_LOG_PAGES_SUPPORTED_ID, (void *)&cbs_data)) {
+ if (cbs_data != NULL) {
+ printf("Log Page Directory\n");
+ /* print the supported pages */
+ if (!strcmp(cfg.output_format, "normal")) {
+ for (i = 0; i < le32_to_cpu(cbs_data->length); i++) {
+ printf("0x%x - %s\n", cbs_data->data[i],
+ nvme_log_id_to_string(cbs_data->data[i]));
+ }
+ } else if (!strcmp(cfg.output_format, "binary")) {
+ d((__u8 *)cbs_data->data, le32_to_cpu(cbs_data->length), 16, 1);
+ } else if (!strcmp(cfg.output_format, "json")) {
+ struct json_object *root;
+ root = json_create_object();
+
+ for (i = 0; i < le32_to_cpu(cbs_data->length); i++) {
+ json_object_add_value_int(root, nvme_log_id_to_string(cbs_data->data[i]),
+ cbs_data->data[i]);
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+ } else
+ fprintf(stderr, "%s: ERROR : WDC : Invalid format, format = %s\n", __func__, cfg.output_format);
+
+ free(cbs_data);
+ } else
+ fprintf(stderr, "%s: ERROR : WDC : NULL_data ptr\n", __func__);
+ } else
+ fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_LOG_PAGES_SUPPORTED_ID);
+
+
+ }
+
+ out:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_get_drive_reason_id(struct nvme_dev *dev, char *drive_reason_id, size_t len)
+{
+ int i, j;
+ int ret;
+ int res_len = 0;
+ struct nvme_id_ctrl ctrl;
+ char *reason_id_str = "reason_id";
+
+ i = sizeof (ctrl.sn) - 1;
+ j = sizeof (ctrl.mn) - 1;
+ memset(drive_reason_id, 0, len);
+ memset(&ctrl, 0, sizeof (struct nvme_id_ctrl));
+ ret = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : nvme_identify_ctrl() failed "
+ "0x%x\n", ret);
+ return -1;
+ }
+ /* Remove trailing spaces from the sn and mn */
+ while (i && ctrl.sn[i] == ' ') {
+ ctrl.sn[i] = '\0';
+ i--;
+ }
+
+ while (j && ctrl.mn[j] == ' ') {
+ ctrl.mn[j] = '\0';
+ j--;
+ }
+
+ res_len = snprintf(drive_reason_id, len, "%s_%s_%s", ctrl.sn, ctrl.mn, reason_id_str);
+ if (len <= res_len) {
+ fprintf(stderr, "ERROR : WDC : cannot format serial number due to data "
+ "of unexpected length\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int wdc_save_reason_id(struct nvme_dev *dev, __u8 *rsn_ident, int size)
+{
+ int ret = 0;
+ char *reason_id_file;
+ char drive_reason_id[PATH_MAX] = {0};
+ char reason_id_path[PATH_MAX] = WDC_REASON_ID_PATH_NAME;
+ struct stat st = {0};
+
+ if (wdc_get_drive_reason_id(dev, drive_reason_id, PATH_MAX) == -1) {
+ fprintf(stderr, "%s: ERROR : failed to get drive reason id\n", __func__);
+ return -1;
+ }
+
+ /* make the nvmecli dir in /usr/local if it doesn't already exist */
+ if (stat(reason_id_path, &st) == -1) {
+ if (mkdir(reason_id_path, 0700) < 0) {
+ fprintf(stderr, "%s: ERROR : failed to mkdir %s : %s\n",
+ __func__, reason_id_path, strerror(errno));
+ return -1;
+ }
+ }
+
+ if (asprintf(&reason_id_file, "%s/%s%s", reason_id_path,
+ drive_reason_id, ".bin") < 0)
+ return -ENOMEM;
+
+ fprintf(stderr, "%s: reason id file = %s\n", __func__, reason_id_file);
+
+ /* save off the error reason identifier to a file in /usr/local/nvmecli */
+ ret = wdc_create_log_file(reason_id_file, rsn_ident, WDC_REASON_ID_ENTRY_LEN);
+ free(reason_id_file);
+
+ return ret;
+}
+
+static int wdc_clear_reason_id(struct nvme_dev *dev)
+{
+ int ret = -1;
+ int verify_file;
+ char *reason_id_file;
+ char drive_reason_id[PATH_MAX] = {0};
+
+ if (wdc_get_drive_reason_id(dev, drive_reason_id, PATH_MAX) == -1) {
+ fprintf(stderr, "%s: ERROR : failed to get drive reason id\n", __func__);
+ return -1;
+ }
+
+ if (asprintf(&reason_id_file, "%s/%s%s", WDC_REASON_ID_PATH_NAME,
+ drive_reason_id, ".bin") < 0)
+ return -ENOMEM;
+
+ /* verify the drive reason id file name and path is valid */
+ verify_file = open(reason_id_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (verify_file < 0) {
+ ret = -1;
+ goto free;
+ }
+ close(verify_file);
+
+ /* remove the reason id file */
+ ret = remove(reason_id_file);
+
+ free:
+ free(reason_id_file);
+
+ return ret;
+}
+
+static int wdc_dump_telemetry_hdr(struct nvme_dev *dev, int log_id, struct nvme_telemetry_log *log_hdr)
+{
+ int ret = 0;
+
+ if (log_id == NVME_LOG_LID_TELEMETRY_HOST)
+ ret = nvme_get_log_create_telemetry_host(dev_fd(dev), log_hdr);
+ else
+ ret = nvme_get_log_telemetry_ctrl(dev_fd(dev), false, 0, 512,
+ (void *)log_hdr);
+
+ if (ret < 0)
+ perror("get-telemetry-log");
+ else if (ret > 0) {
+ nvme_show_status(ret);
+ fprintf(stderr, "%s: ERROR : Failed to acquire telemetry header, ret = %d!\n", __func__, ret);
+ }
+
+ return ret;
+}
+
+static int wdc_do_get_reason_id(struct nvme_dev *dev, char *file, int log_id)
+{
+ int ret;
+ struct nvme_telemetry_log *log_hdr;
+ __u32 log_hdr_size = sizeof(struct nvme_telemetry_log);
+ __u32 reason_id_size = 0;
+
+ log_hdr = (struct nvme_telemetry_log *) malloc(log_hdr_size);
+ if (log_hdr == NULL) {
+ fprintf(stderr, "%s: ERROR : malloc failed, size : 0x%x, status : %s\n", __func__, log_hdr_size, strerror(errno));
+ ret = -1;
+ goto out;
+ }
+ memset(log_hdr, 0, log_hdr_size);
+
+ ret = wdc_dump_telemetry_hdr(dev, log_id, log_hdr);
+ if (ret != 0) {
+ fprintf(stderr, "%s: ERROR : get telemetry header failed, ret : %d\n", __func__, ret);
+ ret = -1;
+ goto out;
+ }
+
+ reason_id_size = sizeof(log_hdr->rsnident);
+
+ if (log_id == NVME_LOG_LID_TELEMETRY_CTRL)
+ wdc_save_reason_id(dev, log_hdr->rsnident, reason_id_size);
+
+ ret = wdc_create_log_file(file, (__u8 *)log_hdr->rsnident, reason_id_size);
+
+out:
+ free(log_hdr);
+ return ret;
+}
+
+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) %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(nand_stats->nand_write_tlc)));
+ printf(" NAND Writes SLC (Bytes) %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(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 %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(nand_stats_v3->nand_write_tlc)));
+ printf(" SLC Units Written %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(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 %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(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 %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(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 %s\n",
+ uint128_t_to_string(
+ le128_to_cpu(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(__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_uint128(root, "NAND Writes TLC (Bytes)",
+ le128_to_cpu(nand_stats->nand_write_tlc));
+ json_object_add_value_uint128(root, "NAND Writes SLC (Bytes)",
+ le128_to_cpu(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_uint64(root, "NAND XOR/RAID Recovery Trigger Events",
+ le64_to_cpu(nand_stats->nand_rec_trigger_event));
+ json_object_add_value_uint64(root, "E2E Error Counter",
+ le64_to_cpu(nand_stats->e2e_error_counter));
+ json_object_add_value_uint64(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_uint128(root, "NAND Writes TLC (Bytes)",
+ le128_to_cpu(nand_stats_v3->nand_write_tlc));
+ json_object_add_value_uint128(root, "NAND Writes SLC (Bytes)",
+ le128_to_cpu(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_uint64(root, "Bad NAND Blocks Count - Raw",
+ le64_to_cpu(temp_raw));
+ json_object_add_value_uint64(root, "NAND XOR Recovery count",
+ le64_to_cpu(nand_stats_v3->xor_recovery_count));
+ json_object_add_value_uint64(root, "UECC Read Error count",
+ le64_to_cpu(nand_stats_v3->uecc_read_error_count));
+ json_object_add_value_uint64(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_uint64(root, "User Data Erase Counts - SLC Min",
+ le64_to_cpu(nand_stats_v3->user_data_erase_counts[0]));
+ json_object_add_value_uint64(root, "User Data Erase Counts - SLC Max",
+ le64_to_cpu(nand_stats_v3->user_data_erase_counts[1]));
+ json_object_add_value_uint64(root, "User Data Erase Counts - TLC Min",
+ le64_to_cpu(nand_stats_v3->user_data_erase_counts[2]));
+ json_object_add_value_uint64(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_uint64(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_uint64(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_uint64(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_uint128(root, "Data Set Management Commands",
+ le128_to_cpu(nand_stats_v3->trim_completions));
+ json_object_add_value_uint64(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_uint64(root, "Soft ECC Error Count",
+ le64_to_cpu(nand_stats_v3->soft_ecc_error_count));
+ json_object_add_value_uint64(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_uint64(root, "Bad System Nand Block Count - Raw",
+ le64_to_cpu(temp_raw));
+ json_object_add_value_uint128(root, "Endurance Estimate",
+ le128_to_cpu(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_uint64(root, "Unaligned I/O",
+ le64_to_cpu(nand_stats_v3->unaligned_IO));
+ json_object_add_value_uint128(root, "Physical Media Units Read",
+ le128_to_cpu(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_uint64(root, "Unsupported Request Error Counter",
+ le64_to_cpu(pcie_stats->unsupportedRequestErrorCount));
+ json_object_add_value_uint64(root, "ECRC Error Status Counter",
+ le64_to_cpu(pcie_stats->ecrcErrorStatusCount));
+ json_object_add_value_uint64(root, "Malformed TLP Status Counter",
+ le64_to_cpu(pcie_stats->malformedTlpStatusCount));
+
+ json_object_add_value_uint64(root, "Receiver Overflow Status Counter",
+ le64_to_cpu(pcie_stats->receiverOverflowStatusCount));
+ json_object_add_value_uint64(root, "Unexpected Completion Status Counter",
+ le64_to_cpu(pcie_stats->unexpectedCmpltnStatusCount));
+ json_object_add_value_uint64(root, "Complete Abort Status Counter",
+ le64_to_cpu(pcie_stats->completeAbortStatusCount));
+ json_object_add_value_uint64(root, "Completion Timeout Status Counter",
+ le64_to_cpu(pcie_stats->cmpltnTimoutStatusCount));
+ json_object_add_value_uint64(root, "Flow Control Error Status Counter",
+ le64_to_cpu(pcie_stats->flowControlErrorStatusCount));
+ json_object_add_value_uint64(root, "Poisoned TLP Status Counter",
+ le64_to_cpu(pcie_stats->poisonedTlpStatusCount));
+ json_object_add_value_uint64(root, "Dlink Protocol Error Status Counter",
+ le64_to_cpu(pcie_stats->dLinkPrtclErrorStatusCount));
+ json_object_add_value_uint64(root, "Advisory Non Fatal Error Status Counter",
+ le64_to_cpu(pcie_stats->advsryNFatalErrStatusCount));
+ json_object_add_value_uint64(root, "Replay Timer TO Status Counter",
+ le64_to_cpu(pcie_stats->replayTimerToStatusCount));
+ json_object_add_value_uint64(root, "Replay Number Rollover Status Counter",
+ le64_to_cpu(pcie_stats->replayNumRolloverStCount));
+ json_object_add_value_uint64(root, "Bad DLLP Status Counter",
+ le64_to_cpu(pcie_stats->badDllpStatusCount));
+ json_object_add_value_uint64(root, "Bad TLP Status Counter",
+ le64_to_cpu(pcie_stats->badTlpStatusCount));
+ json_object_add_value_uint64(root, "Receiver Error Status Counter",
+ le64_to_cpu(pcie_stats->receiverErrStatusCount));
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+static int wdc_do_vs_nand_stats_sn810_2(struct nvme_dev *dev, char *format)
+{
+ int ret;
+ int fmt = -1;
+ uint8_t *data = NULL;
+
+ data = NULL;
+ ret = nvme_get_ext_smart_cloud_log(dev_fd(dev), &data, 0,
+ NVME_NSID_ALL);
+
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : %s : Failed to retreive NAND stats\n", __func__);
+ goto out;
+ } else {
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : %s : invalid output format\n", __func__);
+ ret = fmt;
+ goto out;
+ }
+
+ /* parse the data */
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_ext_smart_cloud_log_normal(data, WDC_SCA_V1_NAND_STATS);
+ break;
+ case JSON:
+ wdc_print_ext_smart_cloud_log_json(data, WDC_SCA_V1_NAND_STATS);
+ break;
+ }
+ }
+
+out:
+ if (data)
+ free(data);
+ return ret;
+}
+
+static int wdc_do_vs_nand_stats(struct nvme_dev *dev, char *format)
+{
+ int ret;
+ int fmt = -1;
+ uint8_t *output = NULL;
+ __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));
+ ret = -1;
+ goto out;
+ }
+
+ ret = nvme_get_log_simple(dev_fd(dev), WDC_NVME_NAND_STATS_LOG_ID,
+ WDC_NVME_NAND_STATS_SIZE, (void*)output);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : %s : Failed to retreive NAND stats\n", __func__);
+ goto out;
+ } else {
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ ret = fmt;
+ goto out;
+ }
+
+ version = output[WDC_NVME_NAND_STATS_SIZE - 2];
+
+ /* parse the data */
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_nand_stats_normal(version, output);
+ break;
+ case JSON:
+ wdc_print_nand_stats_json(version, output);
+ break;
+ }
+ }
+
+out:
+ free(output);
+ return ret;
+}
+
+static int wdc_vs_nand_stats(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve NAND statistics.";
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ __u64 capabilities = 0;
+ uint32_t read_device_id = 0, read_vendor_id = 0;
+ int ret;
+
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_NAND_STATS) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ ret = wdc_get_pci_ids(r, dev, &read_device_id, &read_vendor_id);
+ if (ret < 0)
+ {
+ fprintf(stderr, "ERROR : WDC: %s: failure to get pci ids, ret = %d\n", __func__, ret);
+ return -1;
+ }
+
+ switch (read_device_id) {
+ case WDC_NVME_SN820CL_DEV_ID:
+ ret = wdc_do_vs_nand_stats_sn810_2(dev,
+ cfg.output_format);
+ break;
+ default:
+ ret = wdc_do_vs_nand_stats(dev, cfg.output_format);
+ break;
+ }
+ }
+
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading NAND statistics, ret = %d\n", ret);
+
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_do_vs_pcie_stats(struct nvme_dev *dev,
+ struct wdc_vs_pcie_stats *pcieStatsPtr)
+{
+ int ret;
+ struct nvme_passthru_cmd admin_cmd;
+ int pcie_stats_size = sizeof(struct wdc_vs_pcie_stats);
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_passthru_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(dev_fd(dev), &admin_cmd, NULL);
+
+ return ret;
+}
+
+static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ const char *desc = "Retrieve PCIE statistics.";
+ struct nvme_dev *dev;
+ int ret = 0;
+ nvme_root_t r;
+ __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()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+
+ r = nvme_scan(NULL);
+ 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(r, dev);
+
+ 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(dev, 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:
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+static int wdc_vs_drive_info(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Send a vs-drive-info command.";
+ nvme_root_t r;
+ uint64_t capabilities = 0;
+ struct nvme_dev *dev;
+ int ret;
+ __le32 result;
+ __u16 size;
+ double rev;
+ struct nvme_id_ctrl ctrl;
+ char vsData[32] = {0};
+ char major_rev = 0, minor_rev = 0;
+ __u8 *data = NULL;
+ __u32 ftl_unit_size = 0, tcg_dev_ownership = 0;
+ __u16 boot_spec_major = 0, boot_spec_minor = 0;
+ int fmt = -1;
+ struct json_object *root = NULL;
+ char formatter[41] = { 0 };
+ char rev_str[16] = { 0 };
+ uint32_t read_device_id = -1, read_vendor_id = -1;
+ wdc_nvme_ext_smart_log *ext_smart_log_ptr = NULL;
+
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC %s invalid output format\n", __func__);
+ dev_close(dev);
+ return fmt;
+ }
+
+ /* get the id ctrl data used to fill in drive info below */
+ ret = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC %s: Identify Controller failed\n", __func__);
+ dev_close(dev);
+ return ret;
+ }
+
+ r = nvme_scan(NULL);
+ wdc_check_device(r, dev);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_INFO) == WDC_DRIVE_CAP_INFO) {
+ ret = wdc_get_pci_ids(r, dev, &read_device_id, &read_vendor_id);
+ if (ret < 0)
+ {
+ fprintf(stderr, "ERROR : WDC: %s: failure to get pci ids, ret = %d\n", __func__, ret);
+ goto out;
+ }
+
+ switch (read_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_SN650_DEV_ID:
+ case WDC_NVME_SN650_DEV_ID_1:
+ case WDC_NVME_SN650_DEV_ID_2:
+ case WDC_NVME_SN650_DEV_ID_3:
+ case WDC_NVME_SN650_DEV_ID_4:
+ case WDC_NVME_SN655_DEV_ID:
+ case WDC_NVME_SN560_DEV_ID_1:
+ case WDC_NVME_SN560_DEV_ID_2:
+ case WDC_NVME_SN560_DEV_ID_3:
+ case WDC_NVME_SN550_DEV_ID:
+ case WDC_NVME_ZN350_DEV_ID:
+ case WDC_NVME_ZN350_DEV_ID_1:
+ ret = wdc_do_drive_info(dev, &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);
+ }
+ }
+ break;
+ case WDC_NVME_SN730A_DEV_ID:
+ 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);
+ }
+ break;
+ case WDC_NVME_SN820CL_DEV_ID:
+ /* Get the Drive HW Rev from the C6 Log page */
+ ret = nvme_get_hw_rev_log(dev_fd(dev), &data, 0,
+ NVME_NSID_ALL);
+ if (ret == 0) {
+ wdc_nvme_hw_rev_log *log_data = (wdc_nvme_hw_rev_log *)data;
+ major_rev = log_data->hw_rev_gdr;
+
+ free(data);
+ data = NULL;
+ } else {
+ fprintf(stderr, "ERROR : WDC: %s: failure to get hw revision log\n", __func__);
+ ret = -1;
+ goto out;
+ }
+
+ /* Get the Smart C0 log page */
+ if ((capabilities & WDC_DRIVE_CAP_CLOUD_LOG_PAGE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = nvme_get_ext_smart_cloud_log(dev_fd(dev), &data,
+ 0, NVME_NSID_ALL);
+
+ if (ret == 0) {
+ ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data;
+
+ /* Set the FTL Unit size */
+ ftl_unit_size = le32_to_cpu(ext_smart_log_ptr->ext_smart_ftlus);
+
+ /* Set the Boot Spec Version */
+ boot_spec_major = le16_to_cpu(ext_smart_log_ptr->ext_smart_maj);
+ boot_spec_minor = le16_to_cpu(ext_smart_log_ptr->ext_smart_min);
+
+ /* Set the Drive Ownership Status */
+ tcg_dev_ownership = le32_to_cpu(ext_smart_log_ptr->ext_smart_tcgos);
+ free(data);
+ } else {
+ fprintf(stderr, "ERROR : WDC: %s: failure to get extended smart cloud log\n", __func__);
+ ret = -1;
+ goto out;
+ }
+
+ if (fmt == NORMAL) {
+ printf("Drive HW Revision: %2d\n", major_rev);
+ printf("FTL Unit Size: %d\n", ftl_unit_size);
+ printf("HyperScale Boot Version Spec: %d.%d\n", boot_spec_major, boot_spec_minor);
+ printf("TCG Device Ownership Status: %2d\n", tcg_dev_ownership);
+
+ }
+ else if (fmt == JSON) {
+ root = json_create_object();
+
+ json_object_add_value_int(root, "Drive HW Revison", major_rev);
+ json_object_add_value_int(root, "FTL Unit Size", ftl_unit_size);
+ sprintf(rev_str, "%d.%d", boot_spec_major, boot_spec_minor);
+ json_object_add_value_string(root, "HyperScale Boot Version Spec", rev_str);
+ json_object_add_value_int(root, "TCG Device Ownership Status", tcg_dev_ownership);
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+ }
+
+ break;
+ default:
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ break;
+ }
+ } else {
+ fprintf(stderr, "ERROR : WDC: capability not supported by this device\n");
+ ret = -1;
+ }
+
+out:
+ nvme_show_status(ret);
+ nvme_free_tree(r);
+ dev_close(dev);
+ 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;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ uint64_t capabilities = 0;
+ __u32 hctm_tmt;
+ int temperature, temp_tmt1, temp_tmt2;
+ int ret, fmt = -1;
+
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ r = nvme_scan(NULL);
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ ret = fmt;
+ goto out;
+ }
+
+ /* check if command is supported */
+ wdc_check_device(r, dev);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+ if ((capabilities & WDC_DRIVE_CAP_TEMP_STATS) != WDC_DRIVE_CAP_TEMP_STATS) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ /* get the temperature stats or report errors */
+ ret = nvme_identify_ctrl(dev_fd(dev), &id_ctrl);
+ if (ret != 0)
+ goto out;
+ ret = nvme_get_log_smart(dev_fd(dev), NVME_NSID_ALL, false,
+ &smart_log);
+ if (ret != 0)
+ goto out;
+
+ /* convert from Kelvin to degrees Celsius */
+ temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]) - 273;
+
+ /* retrieve HCTM Thermal Management Temperatures */
+ nvme_get_features_simple(dev_fd(dev), 0x10, 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",
+ dev->name, 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__);
+
+out:
+ nvme_show_status(ret);
+ nvme_free_tree(r);
+ dev_close(dev);
+ 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;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ OPT_ARGS(opts) =
+ {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ /* get capabilities */
+ r = nvme_scan(NULL);
+ wdc_check_device(r, dev);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ /* print command and supported status */
+ printf("WDC Plugin Capabilities for NVME device:%s\n", dev->name);
+ 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 : %s\n",
+ capabilities & WDC_DRIVE_CAP_PURGE ? "Supported" : "Not Supported");
+ printf("purge-monitor : %s\n",
+ capabilities & WDC_DRIVE_CAP_PURGE ? "Supported" : "Not Supported");
+ 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("--C3 Log Page : %s\n",
+ capabilities & WDC_DRIVE_CAP_C3_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 ? "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("get-error-recovery-log : %s\n",
+ capabilities & WDC_DRIVE_CAP_OCP_C1_LOG_PAGE ? "Supported" : "Not Supported");
+ printf("get-dev-capabilities-log : %s\n",
+ capabilities & WDC_DRIVE_CAP_OCP_C4_LOG_PAGE ? "Supported" : "Not Supported");
+ printf("get-unsupported-reqs-log : %s\n",
+ capabilities & WDC_DRIVE_CAP_OCP_C5_LOG_PAGE ? "Supported" : "Not Supported");
+ printf("get-latency-monitor-log : %s\n",
+ capabilities & WDC_DRIVE_CAP_C3_LOG_PAGE ? "Supported" : "Not Supported");
+ printf("cloud-boot-SSD-version : %s\n",
+ capabilities & WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION ? "Supported" : "Not Supported");
+ printf("vs-cloud-log : %s\n",
+ capabilities & WDC_DRIVE_CAP_CLOUD_LOG_PAGE ? "Supported" : "Not Supported");
+ printf("vs-hw-rev-log : %s\n",
+ capabilities & WDC_DRIVE_CAP_HW_REV_LOG_PAGE ? "Supported" : "Not Supported");
+ printf("vs-device_waf : %s\n",
+ capabilities & WDC_DRIVE_CAP_DEVICE_WAF ? "Supported" : "Not Supported");
+ printf("capabilities : Supported\n");
+ nvme_free_tree(r);
+ dev_close(dev);
+ 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;
+ struct nvme_dev *dev;
+ nvme_root_t r;
+ int ret;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ /* get capabilities */
+ r = nvme_scan(NULL);
+ wdc_check_device(r, dev);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_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");
+ }
+
+ nvme_free_tree(r);
+ dev_close(dev);
+ return 0;
+}
+
+static int wdc_cloud_boot_SSD_version(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ const char *desc = "Get Cloud Boot SSD Version command.";
+ const char *namespace_id = "desired namespace id";
+ nvme_root_t r;
+ uint64_t capabilities = 0;
+ struct nvme_dev *dev;
+ int ret;
+ int major = 0, minor = 0;
+ __u8 *data = NULL;
+ wdc_nvme_ext_smart_log *ext_smart_log_ptr = NULL;
+
+ struct config {
+ __u32 namespace_id;
+ };
+
+ struct config cfg = {
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_END()
+ };
+
+ ret = parse_and_open(&dev, argc, argv, desc, opts);
+ if (ret)
+ return ret;
+
+ /* get capabilities */
+ r = nvme_scan(NULL);
+ wdc_check_device(r, dev);
+ capabilities = wdc_get_drive_capabilities(r, dev);
+
+ if ((capabilities & WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION) == WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION) {
+ /* Get the 0xC0 Smart Cloud Attribute V1 log data */
+ ret = nvme_get_ext_smart_cloud_log(dev_fd(dev), &data, 0,
+ cfg.namespace_id);
+
+ ext_smart_log_ptr = (wdc_nvme_ext_smart_log *)data;
+ if (ret == 0) {
+ major = le16_to_cpu(ext_smart_log_ptr->ext_smart_maj);
+ minor = le16_to_cpu(ext_smart_log_ptr->ext_smart_min);
+
+ /* print the version returned from the log page */
+ printf("HyperScale Boot Version: %d.%d\n", major, minor);
+
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read Extended Smart/C0 Log Page data\n");
+ ret = -1;
+ }
+
+ if (data)
+ free(data);
+ } else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ }
+
+ nvme_free_tree(r);
+ dev_close(dev);
+ return ret;
+}
+
+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.";
+ struct nvme_dev *dev;
+ FILE *output_fd;
+ int xfer_size = 0;
+ int len;
+ int err = 0;
+
+ 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 = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ goto ret;
+
+ if (!wdc_enc_check_model(dev)) {
+ 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(dev, 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(dev, 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:
+ dev_close(dev);
+ret:
+ return err;
+}
+
+static int wdc_enc_submit_move_data(struct nvme_dev *dev, 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_passthru_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_admin_passthru(dev_fd(dev), &nvme_cmd, NULL);
+ if (nvme_status_equals(err, NVME_STATUS_TYPE_NVME, 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\n", __func__);
+ nvme_show_status(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_admin_passthru(dev_fd(dev),
+ &nvme_cmd, NULL);
+ if (err != 0) {
+ more = 0;
+ fprintf(stderr, "%s: ERROR : WDC : NVMe Rcv Mgmt ", __func__);
+ nvme_show_status(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(struct nvme_dev *dev, __u8 log_id, __u32 xfer_size, __u32 data_len, FILE *out)
+{
+ __u8 *dump_data;
+ __u32 curr_data_offset, curr_data_len;
+ int i, ret = -1;
+ struct nvme_passthru_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_passthru_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(dev_fd(dev), &admin_cmd,
+ NULL);
+ if (ret != 0) {
+ nvme_show_status(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
new file mode 100644
index 0000000..242cf9a
--- /dev/null
+++ b/plugins/wdc/wdc-nvme.h
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/wdc/wdc-nvme
+
+#if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define WDC_NVME
+
+#define WDC_PLUGIN_VERSION "2.1.2"
+#include "cmd.h"
+
+PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERSION),
+ COMMAND_LIST(
+ ENTRY("cap-diag", "WDC Capture-Diagnostics", wdc_cap_diag)
+ ENTRY("drive-log", "WDC Drive Log", wdc_drive_log)
+ ENTRY("get-crash-dump", "WDC Crash Dump", wdc_get_crash_dump)
+ ENTRY("get-pfail-dump", "WDC Pfail Dump", wdc_get_pfail_dump)
+ ENTRY("id-ctrl", "WDC identify controller", wdc_id_ctrl)
+ ENTRY("purge", "WDC Purge", wdc_purge)
+ ENTRY("purge-monitor", "WDC Purge Monitor", wdc_purge_monitor)
+ ENTRY("vs-internal-log", "WDC Internal Firmware Log", wdc_vs_internal_fw_log)
+ ENTRY("vs-nand-stats", "WDC NAND Statistics", wdc_vs_nand_stats)
+ ENTRY("vs-smart-add-log", "WDC Additional Smart Log", wdc_vs_smart_add_log)
+ ENTRY("clear-pcie-correctable-errors", "WDC Clear PCIe Correctable Error Count", wdc_clear_pcie_correctable_errors)
+ ENTRY("drive-essentials", "WDC Drive Essentials", wdc_drive_essentials)
+ ENTRY("get-drive-status", "WDC Get Drive Status", wdc_drive_status)
+ ENTRY("clear-assert-dump", "WDC Clear Assert Dump", wdc_clear_assert_dump)
+ ENTRY("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)
+ ENTRY("get-latency-monitor-log", "WDC Get Latency Monitor Log Page", wdc_get_latency_monitor_log)
+ ENTRY("get-error-recovery-log", "WDC Get Error Recovery Log Page", wdc_get_error_recovery_log)
+ ENTRY("get-dev-capabilities-log", "WDC Get Device Capabilities Log Page", wdc_get_dev_capabilities_log)
+ ENTRY("get-unsupported-reqs-log", "WDC Get Unsupported Requirements Log Page", wdc_get_unsupported_reqs_log)
+ ENTRY("cloud-boot-SSD-version", "WDC Get the Cloud Boot SSD Version", wdc_cloud_boot_SSD_version)
+ ENTRY("vs-cloud-log", "WDC Get the Cloud Log Page", wdc_vs_cloud_log)
+ ENTRY("vs-hw-rev-log", "WDC Get the Hardware Revision Log Page", wdc_vs_hw_rev_log)
+ ENTRY("vs-device-waf", "WDC Calculate Device Write Amplication Factor", wdc_vs_device_waf)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/wdc/wdc-utils.c b/plugins/wdc/wdc-utils.c
new file mode 100644
index 0000000..38e61ed
--- /dev/null
+++ b/plugins/wdc/wdc-utils.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2017-2018 Western Digital Corporation or its affiliates.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Author: Jeff Lien <jeff.lien@wdc.com>,
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include "wdc-utils.h"
+
+int wdc_UtilsSnprintf(char *buffer, unsigned int sizeOfBuffer, const char *format, ...)
+{
+ int res = 0;
+ va_list vArgs;
+
+ va_start(vArgs, format);
+ res = vsnprintf(buffer, sizeOfBuffer, format, vArgs);
+ va_end(vArgs);
+
+ return res;
+}
+
+void wdc_UtilsDeleteCharFromString(char* buffer, int buffSize, char charToRemove)
+{
+ int i = 0;
+ int count = 0;
+
+ if (!buffer || !buffSize)
+ return;
+
+ /*
+ * Traverse the given string. If current character is not charToRemove,
+ * then place it at index count++
+ */
+ for (i = 0; ((i < buffSize) && (buffer[i] != '\0')); i++) {
+ if (buffer[i] != charToRemove)
+ buffer[count++] = buffer[i];
+ }
+ buffer[count] = '\0';
+}
+
+int wdc_UtilsGetTime(PUtilsTimeInfo timeInfo)
+{
+ time_t currTime;
+ struct tm currTimeInfo;
+
+ if(!timeInfo)
+ return WDC_STATUS_INVALID_PARAMETER;
+
+ tzset();
+ time(&currTime);
+ localtime_r(&currTime, &currTimeInfo);
+
+ timeInfo->year = currTimeInfo.tm_year + 1900;
+ timeInfo->month = currTimeInfo.tm_mon + 1;
+ timeInfo->dayOfWeek = currTimeInfo.tm_wday;
+ timeInfo->dayOfMonth = currTimeInfo.tm_mday;
+ timeInfo->hour = currTimeInfo.tm_hour;
+ timeInfo->minute = currTimeInfo.tm_min;
+ timeInfo->second = currTimeInfo.tm_sec;
+ timeInfo->msecs = 0;
+ timeInfo->isDST = currTimeInfo.tm_isdst;
+#if defined(__GLIBC__) && !defined(__UCLIBC__) && !defined(__MUSL__)
+ timeInfo->zone = -currTimeInfo.tm_gmtoff / 60;
+#else
+ timeInfo->zone = -1 * (timezone / SECONDS_IN_MIN);
+#endif
+
+ return WDC_STATUS_SUCCESS;
+}
+
+int wdc_UtilsCreateDir(char *path)
+{
+ int retStatus;
+ int status = WDC_STATUS_SUCCESS;
+
+ if (!path )
+ return WDC_STATUS_INVALID_PARAMETER;
+
+ retStatus = mkdir(path, 0x999);
+ if (retStatus < 0) {
+ if (errno == EEXIST)
+ status = WDC_STATUS_DIR_ALREADY_EXISTS;
+ else if (errno == ENOENT)
+ status = WDC_STATUS_PATH_NOT_FOUND;
+ else
+ status = WDC_STATUS_CREATE_DIRECTORY_FAILED;
+ }
+
+ return status;
+}
+
+int wdc_WriteToFile(char *fileName, char *buffer, unsigned int bufferLen)
+{
+ int status = WDC_STATUS_SUCCESS;
+ FILE *file;
+ size_t bytesWritten = 0;
+
+ file = fopen(fileName, "ab+");
+ if (!file) {
+ status = WDC_STATUS_UNABLE_TO_OPEN_FILE;
+ goto end;
+ }
+
+ bytesWritten = fwrite(buffer, 1, bufferLen, file);
+ if (bytesWritten != bufferLen)
+ status = WDC_STATUS_UNABLE_TO_WRITE_ALL_DATA;
+
+end:
+ if(file)
+ fclose(file);
+ return status;
+}
+
+/**
+ * Compares the strings ignoring their cases.
+ *
+ * @param pcSrc Points to a null terminated string for comapring.
+ * @param pcDst Points to a null terminated string for comapring.
+ *
+ * @returns zero if the string matches or
+ * 1 if the pcSrc string is lexically higher than pcDst or
+ * -1 if the pcSrc string is lexically lower than pcDst.
+ */
+int wdc_UtilsStrCompare(char *pcSrc, char *pcDst)
+{
+ while ((toupper(*pcSrc) == toupper(*pcDst)) && (*pcSrc != '\0')) {
+ pcSrc++;
+ 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
new file mode 100644
index 0000000..83b208e
--- /dev/null
+++ b/plugins/wdc/wdc-utils.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2017-2018 Western Digital Corporation or its affiliates.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Author: Jeff Lien <jeff.lien@wdc.com>,
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <string.h>
+#include <unistd.h>
+
+/* Create Dir Command Status */
+#define WDC_STATUS_SUCCESS 0
+#define WDC_STATUS_FAILURE -1
+#define WDC_STATUS_INSUFFICIENT_MEMORY -2
+#define WDC_STATUS_INVALID_PARAMETER -3
+#define WDC_STATUS_FILE_SIZE_ZERO -27
+#define WDC_STATUS_UNABLE_TO_WRITE_ALL_DATA -34
+#define WDC_STATUS_DIR_ALREADY_EXISTS -36
+#define WDC_STATUS_PATH_NOT_FOUND -37
+#define WDC_STATUS_CREATE_DIRECTORY_FAILED -38
+#define WDC_STATUS_DELETE_DIRECTORY_FAILED -39
+#define WDC_STATUS_UNABLE_TO_OPEN_FILE -40
+#define WDC_STATUS_UNABLE_TO_OPEN_ZIP_FILE -41
+#define WDC_STATUS_UNABLE_TO_ARCHIVE_EXCEEDED_FILES_LIMIT -256
+#define WDC_STATUS_NO_DATA_FILE_AVAILABLE_TO_ARCHIVE -271
+
+#define WDC_NVME_FIRMWARE_REV_LEN 9 /* added 1 for end delimiter */
+#define WDC_SERIAL_NO_LEN 20
+#define SECONDS_IN_MIN 60
+#define MAX_PATH_LEN 256
+
+typedef struct _UtilsTimeInfo
+{
+ unsigned int year;
+ unsigned int month;
+ unsigned int dayOfWeek;
+ unsigned int dayOfMonth;
+ unsigned int hour;
+ unsigned int minute;
+ unsigned int second;
+ unsigned int msecs;
+ unsigned char isDST; /*0 or 1 */
+ int zone; /* Zone value like +530 or -300 */
+} UtilsTimeInfo, *PUtilsTimeInfo;
+
+int wdc_UtilsSnprintf(char *buffer, unsigned int sizeOfBuffer, const char *format, ...);
+void wdc_UtilsDeleteCharFromString(char* buffer, int buffSize, char charToRemove);
+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..d04481c
--- /dev/null
+++ b/plugins/ymtc/ymtc-nvme.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "nvme.h"
+#include "libnvme.h"
+#include "plugin.h"
+#include "linux/types.h"
+#include "nvme-print.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(struct nvme_dev *dev, __u32 nsid,
+ 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));
+
+ if (!nm) {
+ if (raw)
+ free(raw);
+ return -1;
+ }
+ if (!raw) {
+ free(nm);
+ return -1;
+ }
+ err = nvme_identify_ctrl(dev_fd(dev), &ctrl);
+ if (err) {
+ free(nm);
+ free(raw);
+ 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",
+ dev->name, 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,
+ *(uint16_t *)raw, *(uint16_t *)(raw+2), *(uint16_t *)(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%% %"PRIu32"\n", *nm, *(uint32_t *)raw);
+ /* 08 SI_VD_THERMAL_THROTTLE_STATUS */
+ get_ymtc_smart_info(smart, SI_VD_THERMAL_THROTTLE_STATUS, nm, raw);
+ printf("thermal_throttle_status : %3d%% %d%%, cnt: %"PRIu32"\n", *nm,
+ *raw, *(uint32_t *)(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,
+ *(uint16_t *)raw-273, *(uint16_t *)(raw+2)-273, *(int16_t *)(raw+4)-273);
+ /* 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,
+ *(uint16_t *)raw, *(uint16_t *)(raw+2), *(uint16_t *)(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,
+ *(uint16_t *)raw-273, *(uint16_t *)(raw+2)-273, *(uint16_t *)(raw+4)-273);
+ /* 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%% %u, time: %"PRIu32"\n", *nm,
+ *raw, *(uint32_t *)(raw+1));
+ /* 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;
+ 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 nvme_dev *dev;
+ struct config {
+ __u32 namespace_id;
+ bool raw_binary;
+ };
+ int err;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return err;
+
+ err = nvme_get_nsid_log(dev_fd(dev), false, 0xca, cfg.namespace_id,
+ sizeof(smart_log), &smart_log);
+ if (!err) {
+ if (!cfg.raw_binary)
+ err = show_ymtc_smart_log(dev, cfg.namespace_id, &smart_log);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ if (err > 0)
+ nvme_show_status(err);
+
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/ymtc/ymtc-nvme.h b/plugins/ymtc/ymtc-nvme.h
new file mode 100644
index 0000000..df5d598
--- /dev/null
+++ b/plugins/ymtc/ymtc-nvme.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#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", NVME_VERSION),
+ 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..39f79cb
--- /dev/null
+++ b/plugins/ymtc/ymtc-utils.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#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..f8809ba
--- /dev/null
+++ b/plugins/zns/zns.c
@@ -0,0 +1,1290 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#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 "common.h"
+#include "nvme.h"
+#include "libnvme.h"
+#include "nvme-print.h"
+
+#define CREATE_CMD
+#include "zns.h"
+
+static const char *namespace_id = "Namespace identifier to use";
+static const char dash[100] = { [0 ... 99] = '-' };
+
+static int detect_zns(nvme_ns_t ns, int *out_supported)
+{
+ int err = 0;
+ char *zoned;
+
+ *out_supported = 0;
+
+ zoned = nvme_get_attr(nvme_ns_get_sysfs_dir(ns), "queue/zoned");
+ if (!zoned) {
+ *out_supported = 0;
+ return err;
+ }
+
+ *out_supported = strcmp("host-managed", zoned) == 0;
+ free(zoned);
+
+ return err;
+}
+
+static int print_zns_list_ns(nvme_ns_t ns)
+{
+ int supported;
+ int err = 0;
+
+ err = detect_zns(ns, &supported);
+ if (err) {
+ perror("Failed to enumerate namespace");
+ return err;
+ }
+
+ if (supported) {
+ nvme_show_list_item(ns);
+ }
+
+ return err;
+}
+
+static int print_zns_list(nvme_root_t nvme_root)
+{
+ int err = 0;
+ nvme_host_t h;
+ nvme_subsystem_t s;
+ nvme_ctrl_t c;
+ nvme_ns_t n;
+ nvme_for_each_host(nvme_root, h)
+ {
+ nvme_for_each_subsystem(h, s)
+ {
+ nvme_subsystem_for_each_ns(s, n)
+ {
+ err = print_zns_list_ns(n);
+ if (err)
+ return err;
+ }
+
+ nvme_subsystem_for_each_ctrl(s, c)
+ {
+ nvme_ctrl_for_each_ns(c, n)
+ {
+ err = print_zns_list_ns(n);
+ if (err)
+ return err;
+ }
+ }
+ }
+ }
+
+ return err;
+}
+
+static int list(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = 0;
+ nvme_root_t nvme_root;
+
+ printf("%-21s %-20s %-40s %-9s %-26s %-16s %-8s\n", "Node", "SN",
+ "Model", "Namespace", "Usage", "Format", "FW Rev");
+ printf("%-.21s %-.20s %-.40s %-.9s %-.26s %-.16s %-.8s\n", dash, dash,
+ dash, dash, dash, dash, dash);
+
+ nvme_root = nvme_scan(NULL);
+ if (nvme_root) {
+ err = print_zns_list(nvme_root);
+ nvme_free_tree(nvme_root);
+ } else {
+ fprintf(stderr, "Failed to scan nvme subsystems\n");
+ err = -errno;
+ }
+
+ return err;
+}
+
+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;
+ struct nvme_dev *dev;
+ int 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+
+ err = nvme_zns_identify_ctrl(dev_fd(dev), &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:
+ dev_close(dev);
+ return err;
+}
+
+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;
+ struct nvme_dev *dev;
+ int err = -1;
+
+ struct config {
+ char *output_format;
+ __u32 namespace_id;
+ bool human_readable;
+ bool 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ 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 = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_fd;
+ }
+ }
+
+ err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id, &id_ns);
+ if (err) {
+ nvme_show_status(err);
+ goto close_fd;
+ }
+
+ err = nvme_zns_identify_ns(dev_fd(dev), 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:
+ dev_close(dev);
+ 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";
+ const char *timeout = "timeout value, in milliseconds";
+ struct nvme_dev *dev;
+ int err, zcapc = 0;
+ char *command;
+ __u32 result;
+
+ struct config {
+ __u64 zslba;
+ __u32 namespace_id;
+ bool select_all;
+ __u32 timeout;
+ };
+
+ 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_UINT("timeout", 't', &cfg.timeout, timeout),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ goto ret;
+
+ err = asprintf(&command, "%s-%s", plugin->name, cmd->name);
+ if (err < 0)
+ goto close_dev;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto free;
+ }
+ }
+
+ struct nvme_zns_mgmt_send_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .nsid = cfg.namespace_id,
+ .slba = cfg.zslba,
+ .zsa = zsa,
+ .select_all = cfg.select_all,
+ .zsaso = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = cfg.timeout,
+ .result = &result,
+ };
+ err = nvme_zns_mgmt_send(&args);
+ if (!err) {
+ if (zsa == NVME_ZNS_ZSA_RESET)
+ zcapc = result & 0x1;
+
+ printf("%s: Success, action:%d zone:%"PRIx64" all:%d zcapc:%u nsid:%d\n",
+ command, zsa, (uint64_t)cfg.zslba, (int)cfg.select_all,
+ zcapc, cfg.namespace_id);
+ }
+ else if (err > 0)
+ nvme_show_status(err);
+ else
+ perror(desc);
+free:
+ free(command);
+close_dev:
+ dev_close(dev);
+ret:
+ return err;
+}
+
+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, &id_ns);
+ if (err > 0) {
+ nvme_show_status(err);
+ return -1;
+ } else if (err < 0) {
+ perror("identify namespace");
+ return -1;
+ }
+
+ err = nvme_zns_identify_ns(fd, nsid, &ns);
+ if (err > 0) {
+ nvme_show_status(err);
+ return -1;
+ } else if (err < 0) {
+ perror("zns identify namespace");
+ return -1;
+ }
+
+ nvme_id_ns_flbas_to_lbaf_inuse(id_ns.flbas, &lbaf);
+ 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"\
+ "(for flush action, last lba to flush)";
+ const char *zsaso = "Zone Send Action Specific Option";
+ 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)";
+ const char *timeout = "timeout value, in milliseconds";
+
+ int ffd = STDIN_FILENO, err = -1;
+ struct nvme_dev *dev;
+ void *buf = NULL;
+
+ struct config {
+ __u64 zslba;
+ __u32 namespace_id;
+ bool zsaso;
+ bool select_all;
+ __u8 zsa;
+ int data_len;
+ char *file;
+ __u32 timeout;
+ };
+
+ 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("zsaso", 'o', &cfg.zsaso, zsaso),
+ 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_UINT("timeout", 't', &cfg.timeout, timeout),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_dev;
+ }
+ }
+
+ if (!cfg.zsa) {
+ fprintf(stderr, "zone send action must be specified\n");
+ err = -EINVAL;
+ goto close_dev;
+ }
+
+ if (cfg.zsa == NVME_ZNS_ZSA_SET_DESC_EXT) {
+ if(!cfg.data_len) {
+ int data_len = get_zdes_bytes(dev_fd(dev),
+ cfg.namespace_id);
+
+ if (data_len == 0) {
+ fprintf(stderr,
+ "Zone Descriptor Extensions are not supported\n");
+ goto close_dev;
+ } else if (data_len < 0) {
+ err = data_len;
+ goto close_dev;
+ }
+ cfg.data_len = data_len;
+ }
+ if (posix_memalign(&buf, getpagesize(), cfg.data_len)) {
+ fprintf(stderr, "can not allocate feature payload\n");
+ goto close_dev;
+ }
+ 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_dev;
+ }
+ }
+
+ struct nvme_zns_mgmt_send_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .nsid = cfg.namespace_id,
+ .slba = cfg.zslba,
+ .zsa = cfg.zsa,
+ .select_all = cfg.select_all,
+ .zsaso = cfg.zsaso,
+ .data_len = cfg.data_len,
+ .data = buf,
+ .timeout = cfg.timeout,
+ .result = NULL,
+ };
+ err = nvme_zns_mgmt_send(&args);
+ 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_dev:
+ dev_close(dev);
+ return err;
+}
+
+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";
+ const char *zslba = "starting LBA of the zone for this command";
+ const char *zrwaa = "Allocate Zone Random Write Area to zone";
+ const char *select_all = "send command to all zones";
+ const char *timeout = "timeout value, in milliseconds";
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ __u64 zslba;
+ __u32 namespace_id;
+ bool zrwaa;
+ bool select_all;
+ __u32 timeout;
+ };
+
+ 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("zrwaa", 'r', &cfg.zrwaa, zrwaa),
+ OPT_FLAG("select-all", 'a', &cfg.select_all, select_all),
+ OPT_UINT("timeout", 't', &cfg.timeout, timeout),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_dev;
+ }
+ }
+
+ struct nvme_zns_mgmt_send_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .nsid = cfg.namespace_id,
+ .slba = cfg.zslba,
+ .zsa = NVME_ZNS_ZSA_OPEN,
+ .select_all = cfg.select_all,
+ .zsaso = cfg.zrwaa,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = cfg.timeout,
+ .result = NULL,
+ };
+ err = nvme_zns_mgmt_send(&args);
+ if (!err)
+ printf("zns-open-zone: Success zone slba:%"PRIx64" nsid:%d\n",
+ (uint64_t)cfg.zslba, cfg.namespace_id);
+ else
+ nvme_show_status(err);
+close_dev:
+ dev_close(dev);
+ return err;
+}
+
+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 *zrwaa = "Allocate Zone Random Write Area to zone";
+ const char *data = "optional file for zone extention data (default stdin)";
+ const char *timeout = "timeout value, in milliseconds";
+
+ int ffd = STDIN_FILENO, err;
+ struct nvme_dev *dev;
+ void *buf = NULL;
+ int data_len;
+
+ struct config {
+ __u64 zslba;
+ bool zrwaa;
+ __u32 namespace_id;
+ char *file;
+ __u32 timeout;
+ };
+
+ 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("zrwaa", 'r', &cfg.zrwaa, zrwaa),
+ OPT_FILE("data", 'd', &cfg.file, data),
+ OPT_UINT("timeout", 't', &cfg.timeout, timeout),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_dev;
+ }
+ }
+
+ data_len = get_zdes_bytes(dev_fd(dev), cfg.namespace_id);
+
+ if (!data_len || data_len < 0) {
+ fprintf(stderr,
+ "zone format does not provide descriptor extention\n");
+ errno = EINVAL;
+ err = -1;
+ goto close_dev;
+ }
+
+ buf = calloc(1, data_len);
+ if (!buf) {
+ perror("could not alloc memory for zone desc");
+ err = -ENOMEM;
+ goto close_dev;
+ }
+
+ 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;
+ }
+
+ struct nvme_zns_mgmt_send_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .nsid = cfg.namespace_id,
+ .slba = cfg.zslba,
+ .zsa = NVME_ZNS_ZSA_SET_DESC_EXT,
+ .select_all = 0,
+ .zsaso = cfg.zrwaa,
+ .data_len = data_len,
+ .data = buf,
+ .timeout = cfg.timeout,
+ .result = NULL,
+ };
+ err = nvme_zns_mgmt_send(&args);
+ 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_dev:
+ dev_close(dev);
+ return err;
+}
+
+
+static int zrwa_flush_zone(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Flush Explicit ZRWA Range";
+ const char *slba = "LBA to flush up to";
+ const char *timeout = "timeout value, in milliseconds";
+ struct nvme_dev *dev;
+ int err;
+
+ struct config {
+ __u64 lba;
+ __u32 namespace_id;
+ __u32 timeout;
+ };
+
+ struct config cfg = {};
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_SUFFIX("lba", 'l', &cfg.lba, slba),
+ OPT_UINT("timeout", 't', &cfg.timeout, timeout),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_dev;
+ }
+ }
+
+ struct nvme_zns_mgmt_send_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .nsid = cfg.namespace_id,
+ .slba = cfg.lba,
+ .zsa = NVME_ZNS_ZSA_ZRWA_FLUSH,
+ .select_all = 0,
+ .zsaso = 0,
+ .data_len = 0,
+ .data = NULL,
+ .timeout = cfg.timeout,
+ .result = NULL,
+ };
+ err = nvme_zns_mgmt_send(&args);
+ if (!err)
+ printf("zrwa-flush-zone: Success, lba:%"PRIx64" nsid:%d\n",
+ (uint64_t)cfg.lba, cfg.namespace_id);
+ else
+ nvme_show_status(err);
+close_dev:
+ dev_close(dev);
+ return err;
+}
+
+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;
+ struct nvme_dev *dev;
+ void *data = NULL;
+ int err = -1;
+
+ 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_dev;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_dev;
+ }
+ }
+
+ 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_dev;
+ }
+ 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_dev;
+ }
+ }
+
+ struct nvme_zns_mgmt_recv_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .nsid = cfg.namespace_id,
+ .slba = cfg.zslba,
+ .zra = cfg.zra,
+ .zrasf = cfg.zrasf,
+ .zras_feat = cfg.partial,
+ .data_len = cfg.data_len,
+ .data = data,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = NULL,
+ };
+ err = nvme_zns_mgmt_recv(&args);
+ 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_dev:
+ dev_close(dev);
+ return err;
+}
+
+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 *verbose = "show report zones verbosity";
+
+ enum nvme_print_flags flags;
+ int zdes = 0, err = -1;
+ struct nvme_dev *dev;
+ __u32 report_size;
+ bool huge = false;
+ struct nvme_zone_report *report, *buff;
+
+ unsigned int nr_zones_chunks = 1024, /* 1024 entries * 64 bytes per entry = 64k byte transfer */
+ nr_zones_retrieved = 0,
+ nr_zones,
+ offset,
+ log_len;
+ int total_nr_zones = 0;
+ struct nvme_zns_id_ns id_zns;
+ struct nvme_id_ns id_ns;
+ uint8_t lbaf;
+ __le64 zsze;
+ struct json_object *zone_list = 0;
+
+ struct config {
+ char *output_format;
+ __u64 zslba;
+ __u32 namespace_id;
+ int num_descs;
+ int state;
+ bool verbose;
+ 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("verbose", 'v', &cfg.verbose, verbose),
+ OPT_FLAG("extended", 'e', &cfg.extended, ext),
+ OPT_FLAG("partial", 'p', &cfg.partial, part),
+ OPT_END()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_dev;
+ if (cfg.verbose)
+ flags |= VERBOSE;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_dev;
+ }
+ }
+
+ if (cfg.extended) {
+ zdes = get_zdes_bytes(dev_fd(dev), cfg.namespace_id);
+ if (zdes < 0) {
+ err = zdes;
+ goto close_dev;
+ }
+ }
+
+ err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id, &id_ns);
+ if (err) {
+ nvme_show_status(err);
+ goto close_dev;
+ }
+
+ err = nvme_zns_identify_ns(dev_fd(dev), cfg.namespace_id, &id_zns);
+ if (!err) {
+ /* get zsze field from zns id ns data - needed for offset calculation */
+ nvme_id_ns_flbas_to_lbaf_inuse(id_ns.flbas, &lbaf);
+ zsze = le64_to_cpu(id_zns.lbafe[lbaf].zsze);
+ }
+ else {
+ nvme_show_status(err);
+ goto close_dev;
+ }
+
+ log_len = sizeof(struct nvme_zone_report);
+ buff = calloc(1, log_len);
+ if (!buff) {
+ err = -ENOMEM;
+ goto close_dev;
+ }
+
+ err = nvme_zns_report_zones(dev_fd(dev), cfg.namespace_id, 0,
+ cfg.state, false, false,
+ log_len, buff,
+ NVME_DEFAULT_IOCTL_TIMEOUT, NULL);
+ if (err > 0) {
+ nvme_show_status(err);
+ goto free_buff;
+ }
+ else if (err < 0) {
+ perror("zns report-zones");
+ goto free_buff;
+ }
+
+ total_nr_zones = le64_to_cpu(buff->nr_zones);
+
+ if (cfg.num_descs == -1) {
+ cfg.num_descs = total_nr_zones;
+ }
+
+ nr_zones = cfg.num_descs;
+ if (nr_zones < nr_zones_chunks)
+ nr_zones_chunks = nr_zones;
+
+ log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes));
+ report_size = log_len;
+
+ report = nvme_alloc(report_size, &huge);
+ if (!report) {
+ perror("alloc");
+ err = -ENOMEM;
+ goto close_dev;
+ }
+
+ offset = cfg.zslba;
+ if (flags & JSON)
+ zone_list = json_create_array();
+ else
+ printf("nr_zones: %"PRIu64"\n", (uint64_t)le64_to_cpu(total_nr_zones));
+
+ while (nr_zones_retrieved < nr_zones) {
+ if (nr_zones_retrieved >= nr_zones)
+ break;
+
+ if (nr_zones_retrieved + nr_zones_chunks > nr_zones) {
+ nr_zones_chunks = nr_zones - nr_zones_retrieved;
+ log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes));
+ }
+
+ err = nvme_zns_report_zones(dev_fd(dev), cfg.namespace_id,
+ offset,
+ cfg.state, cfg.extended,
+ cfg.partial, log_len, report,
+ NVME_DEFAULT_IOCTL_TIMEOUT, NULL);
+ if (err > 0) {
+ nvme_show_status(err);
+ break;
+ }
+
+ if (!err)
+ nvme_show_zns_report_zones(report, nr_zones_chunks,
+ zdes, log_len, flags, zone_list);
+
+ nr_zones_retrieved += nr_zones_chunks;
+ offset = le64_to_cpu(report->entries[nr_zones_chunks-1].zslba) + zsze;
+ }
+
+ if (flags & JSON)
+ json_nvme_finish_zone_list(total_nr_zones, zone_list);
+
+ nvme_free(report, huge);
+
+free_buff:
+ free(buff);
+close_dev:
+ dev_close(dev);
+ return err;
+}
+
+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, dfd = STDIN_FILENO, mfd = STDIN_FILENO;
+ unsigned int lba_size, meta_size;
+ void *buf = NULL, *mbuf = NULL;
+ __u16 nblocks, control = 0;
+ struct nvme_dev *dev;
+ __u64 result;
+ __u8 lba_index;
+ 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;
+ bool limited_retry;
+ bool fua;
+ __u32 namespace_id;
+ __u64 ref_tag;
+ __u16 lbat;
+ __u16 lbatm;
+ __u8 prinfo;
+ bool piremap;
+ bool 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_SUFFIX("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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ if (!cfg.data_size) {
+ fprintf(stderr, "Append size not provided\n");
+ errno = EINVAL;
+ goto close_dev;
+ }
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_dev;
+ }
+ }
+
+ err = nvme_identify_ns(dev_fd(dev), cfg.namespace_id, &ns);
+ if (err) {
+ nvme_show_status(err);
+ goto close_dev;
+ }
+
+ nvme_id_ns_flbas_to_lbaf_inuse(ns.flbas, &lba_index);
+ lba_size = 1 << ns.lbaf[lba_index].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_dev;
+ }
+
+ meta_size = ns.lbaf[lba_index].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_dev;
+ }
+
+ if (cfg.prinfo > 0xf) {
+ fprintf(stderr, "Invalid value for prinfo:%#x\n", cfg.prinfo);
+ errno = EINVAL;
+ goto close_dev;
+ }
+
+ if (cfg.data) {
+ dfd = open(cfg.data, O_RDONLY);
+ if (dfd < 0) {
+ perror(cfg.data);
+ goto close_dev;
+ }
+ }
+
+ 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 free_data;
+ }
+ }
+
+ 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_IO_LR;
+ if (cfg.fua)
+ control |= NVME_IO_FUA;
+ if (cfg.piremap)
+ control |= NVME_IO_ZNS_APPEND_PIREMAP;
+
+ struct nvme_zns_append_args args = {
+ .args_size = sizeof(args),
+ .fd = dev_fd(dev),
+ .nsid = cfg.namespace_id,
+ .zslba = cfg.zslba,
+ .nlb = nblocks,
+ .control = control,
+ .ilbrt_u64 = cfg.ref_tag,
+ .lbat = cfg.lbat,
+ .lbatm = cfg.lbatm,
+ .data_len = cfg.data_size,
+ .data = buf,
+ .metadata_len = cfg.metadata_size,
+ .metadata = mbuf,
+ .timeout = NVME_DEFAULT_IOCTL_TIMEOUT,
+ .result = &result,
+ };
+
+ gettimeofday(&start_time, NULL);
+ err = nvme_zns_append(&args);
+ 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_dev:
+ dev_close(dev);
+ return err;
+}
+
+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;
+ struct nvme_dev *dev;
+ int 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()
+ };
+
+ err = parse_and_open(&dev, argc, argv, desc, opts);
+ if (err)
+ return errno;
+
+ flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+
+ if (!cfg.namespace_id) {
+ err = nvme_get_nsid(dev_fd(dev), &cfg.namespace_id);
+ if (err < 0) {
+ perror("get-namespace-id");
+ goto close_fd;
+ }
+ }
+
+ err = nvme_get_log_zns_changed_zones(dev_fd(dev), cfg.namespace_id,
+ cfg.rae, &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:
+ dev_close(dev);
+ return err;
+}
diff --git a/plugins/zns/zns.h b/plugins/zns/zns.h
new file mode 100644
index 0000000..77bfdd6
--- /dev/null
+++ b/plugins/zns/zns.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#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", NVME_VERSION),
+ COMMAND_LIST(
+ ENTRY("list", "List all NVMe devices with Zoned Namespace Command Set support", list)
+ ENTRY("id-ctrl", "Send NVMe Identify Zoned Namespace Controller, display structure", id_ctrl)
+ ENTRY("id-ns", "Send NVMe Identify Zoned Namespace Namespace, display structure", id_ns)
+ ENTRY("report-zones", "Report zones associated to a Zoned Namespace", report_zones)
+ ENTRY("reset-zone", "Reset one or more zones", reset_zone)
+ ENTRY("close-zone", "Close one or more zones", close_zone)
+ ENTRY("finish-zone", "Finishe one or more zones", finish_zone)
+ ENTRY("open-zone", "Open one or more zones", open_zone)
+ ENTRY("offline-zone", "Offline one or more zones", offline_zone)
+ ENTRY("set-zone-desc", "Attach zone descriptor extension data to a zone", set_zone_desc)
+ ENTRY("zrwa-flush-zone", "Flush LBAs associated with a ZRWA to a zone.", zrwa_flush_zone)
+ ENTRY("changed-zone-list", "Retrieve the changed zone list log", changed_zone_list)
+ ENTRY("zone-mgmt-recv", "Send the zone management receive command", zone_mgmt_recv)
+ ENTRY("zone-mgmt-send", "Send the zone management send command", zone_mgmt_send)
+ ENTRY("zone-append", "Append data and metadata (if applicable) to a zone", zone_append)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"