summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2021-07-02 20:40:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2021-07-02 20:40:30 +0000
commitdc597ce8df5ae6efd2728a2d7ba7d92486028f79 (patch)
tree55b9e9257eba4579667f9522368aa29f5be6754a /plugins
parentInitial commit. (diff)
downloadnvme-cli-dc597ce8df5ae6efd2728a2d7ba7d92486028f79.tar.xz
nvme-cli-dc597ce8df5ae6efd2728a2d7ba7d92486028f79.zip
Adding upstream version 1.12.upstream/1.12
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--plugins/dera/dera-nvme.c211
-rw-r--r--plugins/dera/dera-nvme.h17
-rw-r--r--plugins/huawei/huawei-nvme.c374
-rw-r--r--plugins/huawei/huawei-nvme.h18
-rw-r--r--plugins/intel/intel-nvme.c1316
-rw-r--r--plugins/intel/intel-nvme.h23
-rw-r--r--plugins/lnvm/lnvm-nvme.c436
-rw-r--r--plugins/lnvm/lnvm-nvme.h27
-rw-r--r--plugins/memblaze/memblaze-nvme.c759
-rw-r--r--plugins/memblaze/memblaze-nvme.h27
-rw-r--r--plugins/memblaze/memblaze-utils.h164
-rw-r--r--plugins/micron/micron-nvme.c1180
-rw-r--r--plugins/micron/micron-nvme.h21
-rw-r--r--plugins/netapp/netapp-nvme.c639
-rw-r--r--plugins/netapp/netapp-nvme.h18
-rw-r--r--plugins/scaleflux/sfx-nvme.c873
-rw-r--r--plugins/scaleflux/sfx-nvme.h25
-rw-r--r--plugins/seagate/seagate-diag.h269
-rw-r--r--plugins/seagate/seagate-nvme.c1397
-rw-r--r--plugins/seagate/seagate-nvme.h45
-rw-r--r--plugins/shannon/shannon-nvme.c374
-rw-r--r--plugins/shannon/shannon-nvme.h20
-rw-r--r--plugins/toshiba/toshiba-nvme.c563
-rw-r--r--plugins/toshiba/toshiba-nvme.h20
-rw-r--r--plugins/transcend/transcend-nvme.c92
-rw-r--r--plugins/transcend/transcend-nvme.h20
-rw-r--r--plugins/virtium/virtium-nvme.c1044
-rw-r--r--plugins/virtium/virtium-nvme.h21
-rw-r--r--plugins/wdc/wdc-nvme.c5663
-rw-r--r--plugins/wdc/wdc-nvme.h38
-rw-r--r--plugins/wdc/wdc-utils.c146
-rw-r--r--plugins/wdc/wdc-utils.h77
32 files changed, 15917 insertions, 0 deletions
diff --git a/plugins/dera/dera-nvme.c b/plugins/dera/dera-nvme.c
new file mode 100644
index 0000000..dbb40cc
--- /dev/null
+++ b/plugins/dera/dera-nvme.c
@@ -0,0 +1,211 @@
+#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 "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+
+#include "argconfig.h"
+#include "suffix.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_passthru(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ if (!err && result) {
+ *result = cmd.result;
+ }
+
+ return err;
+}
+
+static int get_status(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int fd, err;
+ struct nvme_dera_smart_info_log log;
+ enum dera_device_status state = DEVICE_STATUS_FATAL_ERROR;
+ char *desc = "Get the Dera device status";
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ err = nvme_get_log(fd, 0xffffffff, 0xc0, false, 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(fd, &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 >= 0 && 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)
+ fprintf(stderr, "\nNVMe status:%s(0x%x)\n", nvme_status_to_string(err), err);
+
+ return err;
+}
+
diff --git a/plugins/dera/dera-nvme.h b/plugins/dera/dera-nvme.h
new file mode 100644
index 0000000..dc54fab
--- /dev/null
+++ b/plugins/dera/dera-nvme.h
@@ -0,0 +1,17 @@
+#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"),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve Dera SMART Log, show it", get_status, "stat")
+ )
+);
+
+#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..482ea0d
--- /dev/null
+++ b/plugins/huawei/huawei-nvme.c
@@ -0,0 +1,374 @@
+/*
+ * 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 "linux/nvme_ioctl.h"
+
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+#include "json.h"
+
+#include "argconfig.h"
+#include "suffix.h"
+#include <sys/ioctl.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;
+ int 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;
+ item->nsid = nvme_get_nsid(fd);
+ err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
+ if (err)
+ return err;
+
+ err = fstat(fd, &nvme_stat_info);
+ if (err < 0)
+ return err;
+
+ strcpy(item->node, node);
+ 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_array *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)
+{
+ long long int lba = 1 << list_item.ns.lbaf[(list_item.ns.flbas & 0x0f)].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, fd, 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()
+ };
+
+ argconfig_parse(argc, argv, desc, opts);
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt != JSON && fmt != NORMAL)
+ return -EINVAL;
+
+ n = scandir("/dev", &devices, scan_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");
+ return ENOMEM;
+ }
+
+ for (i = 0; i < n; i++) {
+ snprintf(path, sizeof(path), "/dev/%s", devices[i]->d_name);
+ fd = open(path, O_RDONLY);
+ ret = huawei_get_nvme_info(fd, &list_items[huawei_num], path);
+ if (ret)
+ return ret;
+ if (list_items[huawei_num].huawei_device == true) {
+ huawei_num++;
+ }
+ }
+
+ 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);
+ }
+
+ for (i = 0; i < n; i++)
+ free(devices[i]);
+ free(devices);
+ free(list_items);
+
+ return 0;
+}
+
+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..7aac90c
--- /dev/null
+++ b/plugins/huawei/huawei-nvme.h
@@ -0,0 +1,18 @@
+#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"),
+ 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/intel/intel-nvme.c b/plugins/intel/intel-nvme.c
new file mode 100644
index 0000000..8217c46
--- /dev/null
+++ b/plugins/intel/intel-nvme.c
@@ -0,0 +1,1316 @@
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "linux/nvme_ioctl.h"
+
+#include "common.h"
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "json.h"
+#include "plugin.h"
+
+#include "argconfig.h"
+#include "suffix.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_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_float(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);
+
+ json_object_add_value_object(root, "Device stats", dev_stats);
+
+ json_print_object(root, NULL);
+ json_free_object(root);
+}
+
+static void show_intel_smart_log(struct nvme_additional_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 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));
+}
+
+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;
+ int err, fd;
+
+ struct config {
+ __u32 namespace_id;
+ int raw_binary;
+ int 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ err = nvme_get_log(fd, cfg.namespace_id, 0xca, false,
+ sizeof(smart_log), &smart_log);
+ if (!err) {
+ if (cfg.json)
+ show_intel_smart_log_jsn(&smart_log, cfg.namespace_id, devicename);
+ else if (!cfg.raw_binary)
+ show_intel_smart_log(&smart_log, cfg.namespace_id, devicename);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ 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";
+
+ char log[512];
+ int err, fd;
+
+ struct config {
+ int raw_binary;
+ };
+
+ struct config cfg = {
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ err = nvme_get_log(fd, NVME_NSID_ALL, 0xdd, false,
+ 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ 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;
+ int err, fd;
+
+ const char *desc = "Get Intel Marketing Name log and show it.";
+ const char *raw = "dump output in binary format";
+ struct config {
+ int raw_binary;
+ };
+
+ struct config cfg = {
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ err = nvme_get_log(fd, NVME_NSID_ALL, 0xc5, false,
+ 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ return err;
+}
+
+struct intel_lat_stats {
+ __u16 maj;
+ __u16 min;
+ __u32 data[1216];
+};
+
+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 const 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 const 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)
+{
+ if (bound_type != NOINF) {
+ snprintf(buffer, 5, "%s", bound_type ? "+INF" : "-INF");
+ return;
+ }
+ char *string;
+
+ switch (unit) {
+ case US:
+ string = "us";
+ break;
+ case MS:
+ string = "ms";
+ break;
+ case S:
+ string = "s";
+ break;
+ default:
+ string = "_s";
+ break;
+ }
+ snprintf(buffer, 11, "%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_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.
+ */
+static int lat_stats_log_scale(int i)
+{
+ static const int LATENCY_STATS_V4_BASE_BITS = 6;
+ static const int LATENCY_STATS_V4_BASE_VAL = (
+ 1 << LATENCY_STATS_V4_BASE_BITS);
+
+ // 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_add_value_object(bucket_list,
+ "bucket", bucket);
+ json_object_add_value_int(bucket, "id", id);
+
+ set_unit_string(buffer, lower_us,
+ get_seconds_magnitude(lower_us), start_type);
+ json_object_add_value_string(bucket, "start", buffer);
+
+ set_unit_string(buffer, upper_us,
+ get_seconds_magnitude(upper_us), end_type);
+ json_object_add_value_string(bucket, "end", buffer);
+
+ json_object_add_value_int(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_create_object();
+
+ 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_create_object();
+
+ 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(struct intel_lat_stats *stats, int write)
+{
+ switch (stats->maj) {
+ case 3:
+ json_lat_stats_3_0(stats, write);
+ break;
+ case 4:
+ switch (stats->min) {
+ 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"
+ "Defaulting to format for rev4.0"),
+ 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(struct intel_lat_stats *stats, int write)
+{
+ static const int separator_length = 50;
+
+ printf("Intel IO %s Command Latency Statistics\n",
+ write ? "Write" : "Read");
+ printf("Major Revision : %u\nMinor Revision : %u\n",
+ stats->maj, stats->min);
+ print_dash_separator(separator_length);
+ printf("%-12s%-12s%-12s%-20s\n", "Bucket", "Start", "End", "Value");
+ print_dash_separator(separator_length);
+
+ switch (stats->maj) {
+ case 3:
+ show_lat_stats_3_0(stats);
+ break;
+ case 4:
+ switch (stats->min) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ show_lat_stats_4_0(stats);
+ break;
+ default:
+ printf(("Unsupported minor revision (%u.%u)\n"
+ "Defaulting to format for rev4.0"),
+ stats->maj, stats->min);
+ 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)
+{
+ struct intel_lat_stats stats;
+ enum nvme_print_flags flags;
+ int err, fd;
+
+ const char *desc = "Get Intel Latency Statistics log and show it.";
+ const char *raw = "dump output in binary format";
+ const char *write = "Get write statistics (read default)";
+ struct config {
+ char *output_format;
+ int raw_binary;
+ int write;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("write", 'w', &cfg.write, write),
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output format: normal|json|binary"),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ err = flags = validate_output_format(cfg.output_format);
+ if (flags < 0)
+ goto close_fd;
+
+ if (cfg.raw_binary)
+ flags = BINARY;
+
+ err = nvme_get_log(fd, NVME_NSID_ALL, cfg.write ? 0xc2 : 0xc1,
+ false, sizeof(stats), &stats);
+ if (!err) {
+ if (flags & JSON)
+ json_lat_stats(&stats, cfg.write);
+ else if (flags & BINARY)
+ d_raw((unsigned char *)&stats, sizeof(stats));
+ else
+ show_lat_stats(&stats, cfg.write);
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+
+close_fd:
+ close(fd);
+ 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);
+ 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, fd, 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;
+
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ free(intel);
+ return fd;
+ }
+
+ if (cfg.log > 2 || cfg.core > 4 || cfg.lnum > 255) {
+ free(intel);
+ return EINVAL;
+ }
+
+ if (!cfg.file) {
+ err = setup_file(f, cfg.file, fd, cfg.log);
+ if (err)
+ goto out;
+ 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);
+
+ err = read_header(&cmd, buf, fd, 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, fd, &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, fd, 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, fd, 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, fd, 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, fd, buf);
+ if (err)
+ goto out;
+ }
+ }
+ }
+ err = 0;
+ out:
+ if (err > 0) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ } else if (err < 0) {
+ perror("intel log");
+ err = EIO;
+ } else
+ printf("Successfully wrote log to %s\n", cfg.file);
+ free(intel);
+ return err;
+}
+
+static int enable_lat_stats_tracking(int argc, char **argv,
+ struct command *command, struct plugin *plugin)
+{
+ int err, fd;
+ 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;
+ __u32 result;
+ void *buf = NULL;
+
+ struct config {
+ bool enable, disable;
+ };
+
+ struct config cfg = {
+ .enable = false,
+ .disable = false,
+ };
+
+ const struct argconfig_commandline_options command_line_options[] = {
+ {"enable", 'e', "", CFG_NONE, &cfg.enable, no_argument, enable_desc},
+ {"disable", 'd', "", CFG_NONE, &cfg.disable, no_argument, disable_desc},
+ {NULL}
+ };
+
+ fd = parse_and_open(argc, argv, desc, command_line_options);
+
+ enum Option {
+ None = -1,
+ True = 1,
+ False = 0,
+ };
+
+ enum Option option = None;
+
+ if (cfg.enable && cfg.disable)
+ printf("Cannot enable and disable simultaneously.");
+ else if (cfg.enable || cfg.disable)
+ option = cfg.enable;
+
+ if (fd < 0)
+ return fd;
+ switch (option) {
+ case None:
+ err = nvme_get_feature(fd, nsid, fid, sel, cdw11, data_len, buf,
+ &result);
+ if (!err) {
+ printf(
+ "Latency Statistics Tracking (FID 0x%X) is currently (%i).\n",
+ fid, result);
+ } else {
+ printf("Could not read feature id 0xE2.\n");
+ return err;
+ }
+ break;
+ case True:
+ case False:
+ err = nvme_set_feature(fd, nsid, fid, option, cdw12, save,
+ data_len, buf, &result);
+ if (err > 0) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ } else if (err < 0) {
+ perror("Enable latency tracking");
+ fprintf(stderr, "Command failed while parsing.\n");
+ } else {
+ printf("Successfully set enable bit for FID (0x%X) to %i.\n",
+ fid, option);
+ }
+ break;
+ default:
+ printf("%d not supported.\n", option);
+ return EINVAL;
+ }
+ return fd;
+}
diff --git a/plugins/intel/intel-nvme.h b/plugins/intel/intel-nvme.h
new file mode 100644
index 0000000..335e901
--- /dev/null
+++ b/plugins/intel/intel-nvme.h
@@ -0,0 +1,23 @@
+#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"),
+ 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("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/lnvm/lnvm-nvme.c b/plugins/lnvm/lnvm-nvme.c
new file mode 100644
index 0000000..3678176
--- /dev/null
+++ b/plugins/lnvm/lnvm-nvme.c
@@ -0,0 +1,436 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+
+#include "nvme-lightnvm.h"
+
+#include "argconfig.h"
+#include "suffix.h"
+
+#define CREATE_CMD
+#include "lnvm-nvme.h"
+
+static int lnvm_init(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Initialize LightNVM device. A LightNVM/Open-Channel SSD"\
+ " must have a media manager associated before it can"\
+ " be exposed to the user. The default is to initialize"
+ " the general media manager on top of the device.\n\n"
+ "Example:"
+ " lnvm-init -d nvme0n1";
+ const char *devname = "identifier of desired device. e.g. nvme0n1.";
+ const char *mmtype = "media manager to initialize on top of device. Default: gennvm.";
+ int ret;
+
+ struct config {
+ char *devname;
+ char *mmtype;
+ };
+
+ struct config cfg = {
+ .devname = "",
+ .mmtype = "gennvm",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("device-name", 'd', "DEVICE", &cfg.devname, devname),
+ OPT_STRING("mediamgr-name", 'm', "MM", &cfg.mmtype, mmtype),
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ if (!strlen(cfg.devname)) {
+ fprintf(stderr, "device name missing\n");
+ return -EINVAL;
+ }
+
+ return lnvm_do_init(cfg.devname, cfg.mmtype);
+}
+
+static int lnvm_list(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "List all devices registered with LightNVM.";
+ int ret;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ return lnvm_do_list_devices();
+}
+
+static int lnvm_info(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Show general information and registered target types with LightNVM";
+ int ret;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ return lnvm_do_info();
+}
+
+static int lnvm_id_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Send an Identify Geometry command to the "\
+ "given LightNVM device, returns properties of the specified "\
+ "namespace in either human-readable or binary format.";
+ const char *raw_binary = "show infos in binary format";
+ const char *human_readable = "show infos in readable format";
+ const char *namespace_id = "identifier of desired namespace. default: 1";
+ unsigned int flags = 0;
+ int fd;
+
+ struct config {
+ __u32 namespace_id;
+ int raw_binary;
+ int human_readable;
+ };
+
+ struct config cfg = {
+ .namespace_id = 1,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_binary),
+ OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (cfg.human_readable)
+ flags |= VERBOSE;
+ else if (cfg.raw_binary)
+ flags |= BINARY;
+
+ return lnvm_do_id_ns(fd, cfg.namespace_id, flags);
+}
+
+static int lnvm_chunk_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Retrieve the chunk information log for the "\
+ "specified given LightNVM device, returns in either "\
+ "human-readable or binary format.\n"\
+ "This will request Geometry first to get the "\
+ "num_grp,num_pu,num_chk first to figure out the total size "\
+ "of the log pages."\
+ ;
+ const char *output_format = "Output format: normal|binary";
+ const char *human_readable = "Print normal in readable format";
+ int err, fmt, fd;
+ struct nvme_nvm_id20 geo;
+ struct nvme_nvm_chunk_desc *chunk_log;
+ __u32 nsid;
+ __u32 data_len;
+ unsigned int flags = 0;
+
+ struct config {
+ char *output_format;
+ int human_readable;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_FLAG("human-readable",'H', &cfg.human_readable, human_readable),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ fmt = validate_output_format(cfg.output_format);
+ if (fmt < 0) {
+ err = fmt;
+ goto close;
+ }
+
+ if (fmt == BINARY)
+ flags |= BINARY;
+ else if (cfg.human_readable)
+ flags |= VERBOSE;
+
+ nsid = nvme_get_nsid(fd);
+
+ /*
+ * It needs to figure out how many bytes will be requested by this
+ * subcommand by the (num_grp * num_pu * num_chk) from the Geometry.
+ */
+ err = lnvm_get_identity(fd, nsid, (struct nvme_nvm_id *) &geo);
+ if (err)
+ goto close;
+
+ data_len = (geo.num_grp * geo.num_pu * geo.num_chk) *
+ sizeof(struct nvme_nvm_chunk_desc);
+ chunk_log = malloc(data_len);
+ if (!chunk_log) {
+ fprintf(stderr, "cound not alloc for chunk log %dbytes\n",
+ data_len);
+ err = -ENOMEM;
+ goto close;
+ }
+
+ err = lnvm_do_chunk_log(fd, nsid, data_len, chunk_log, flags);
+ if (err)
+ fprintf(stderr, "get log page for chunk information failed\n");
+
+ free(chunk_log);
+close:
+ close(fd);
+ return err;
+}
+
+static int lnvm_create_tgt(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Instantiate a target on top of a LightNVM enabled device.";
+ const char *devname = "identifier of desired device. e.g. nvme0n1.";
+ const char *tgtname = "target name of the device to initialize. e.g. target0.";
+ const char *tgttype = "identifier of target type. e.g. pblk.";
+ const char *lun_begin = "Define begin of luns to use for target.";
+ const char *lun_end = "Define set of luns to use for target.";
+ const char *over_prov = "Define over-provision percentage for target.";
+ const char *flag_factory = "Create target in factory mode";
+ int flags;
+ int ret;
+
+ struct config {
+ char *devname;
+ char *tgtname;
+ char *tgttype;
+ __u32 lun_begin;
+ __u32 lun_end;
+ __u32 over_prov;
+
+ /* flags */
+ __u32 factory;
+ };
+
+ struct config cfg = {
+ .devname = "",
+ .tgtname = "",
+ .tgttype = "",
+ .lun_begin = -1,
+ .lun_end = -1,
+ .over_prov = -1,
+ .factory = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("device-name", 'd', "DEVICE", &cfg.devname, devname),
+ OPT_STRING("target-name", 'n', "TARGET", &cfg.tgtname, tgtname),
+ OPT_STRING("target-type", 't', "TARGETTYPE", &cfg.tgttype, tgttype),
+ OPT_UINT("lun-begin", 'b', &cfg.lun_begin, lun_begin),
+ OPT_UINT("lun-end", 'e', &cfg.lun_end, lun_end),
+ OPT_UINT("over-prov", 'o', &cfg.over_prov, over_prov),
+ OPT_FLAG("factory", 'f', &cfg.factory, flag_factory),
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ if (!strlen(cfg.devname)) {
+ fprintf(stderr, "device name missing\n");
+ return -EINVAL;
+ }
+ if (!strlen(cfg.tgtname)) {
+ fprintf(stderr, "target name missing\n");
+ return -EINVAL;
+ }
+ if (!strlen(cfg.tgttype)) {
+ fprintf(stderr, "target type missing\n");
+ return -EINVAL;
+ }
+
+ flags = 0;
+ if (cfg.factory)
+ flags |= NVM_TARGET_FACTORY;
+
+ return lnvm_do_create_tgt(cfg.devname, cfg.tgtname, cfg.tgttype, cfg.lun_begin, cfg.lun_end, cfg.over_prov, flags);
+}
+
+static int lnvm_remove_tgt(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Remove an initialized LightNVM target.";
+ const char *tgtname = "target name of the device to remove. e.g. target0.";
+ int ret;
+
+ struct config {
+ char *tgtname;
+ };
+
+ struct config cfg = {
+ .tgtname = "",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("target-name", 'n', "TARGET", &cfg.tgtname, tgtname),
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ if (!strlen(cfg.tgtname)) {
+ fprintf(stderr, "target name missing\n");
+ return -EINVAL;
+ }
+
+ return lnvm_do_remove_tgt(cfg.tgtname);
+}
+
+static int lnvm_factory_init(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Factory initialize a LightNVM enabled device.";
+ const char *devname = "identifier of desired device. e.g. nvme0n1.";
+ const char *erase_only_marked = "only erase marked blocks. default: all blocks.";
+ const char *host_marks = "remove host side blocks list. default: keep.";
+ const char *bb_marks = "remove grown bad blocks list. default: keep";
+ int ret;
+
+ struct config {
+ char *devname;
+ int erase_only_marked;
+ int clear_host_marks;
+ int clear_bb_marks;
+ };
+
+ struct config cfg = {
+ .devname = "",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("device-name", 'd', "DEVICE", &cfg.devname, devname),
+ OPT_FLAG("erase-only-marked", 'e', &cfg.erase_only_marked, erase_only_marked),
+ OPT_FLAG("clear-host-side-blks", 's', &cfg.clear_host_marks, host_marks),
+ OPT_FLAG("clear-bb-blks", 'b', &cfg.clear_bb_marks, bb_marks),
+ OPT_END()
+ };
+
+ ret = argconfig_parse(argc, argv, desc, opts);
+ if (ret < 0)
+ return ret;
+
+ if (!strlen(cfg.devname)) {
+ fprintf(stderr, "device name missing\n");
+ return -EINVAL;
+ }
+
+ return lnvm_do_factory_init(cfg.devname, cfg.erase_only_marked,
+ cfg.clear_host_marks, cfg.clear_bb_marks);
+}
+
+static int lnvm_get_bbtbl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Receive bad block table from a LightNVM compatible"\
+ " device.";
+ const char *namespace = "(optional) desired namespace";
+ const char *ch = "channel identifier";
+ const char *lun = "lun identifier (within a channel)";
+ const char *raw_binary = "show infos in binary format";
+ unsigned int fd, flags = 0;
+
+ struct config {
+ __u32 namespace_id;
+ __u16 lunid;
+ __u16 chid;
+ int raw_binary;
+ };
+
+ struct config cfg = {
+ .namespace_id = 1,
+ .lunid = 0,
+ .chid = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
+ OPT_SHRT("channel-id", 'c', &cfg.chid, ch),
+ OPT_SHRT("lun-id", 'l', &cfg.lunid, lun),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw_binary),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (cfg.raw_binary)
+ flags |= BINARY;
+
+ return lnvm_do_get_bbtbl(fd, cfg.namespace_id, cfg.lunid, cfg.chid, flags);
+}
+
+static int lnvm_set_bbtbl(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc = "Update bad block table on a LightNVM compatible"\
+ " device.";
+ const char *namespace = "(optional) desired namespace";
+ const char *ch = "channel identifier";
+ const char *lun = "lun identifier (within a channel)";
+ const char *pln = "plane identifier (within a lun)";
+ const char *blk = "block identifier (within a plane)";
+ const char *value = "value to update the specific block to.";
+ int fd;
+
+ struct config {
+ __u32 namespace_id;
+ __u16 lunid;
+ __u16 chid;
+ __u16 plnid;
+ __u16 blkid;
+ __u16 value;
+ };
+
+ struct config cfg = {
+ .namespace_id = 1,
+ .lunid = 0,
+ .chid = 0,
+ .plnid = 0,
+ .blkid = 0,
+ .value = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
+ OPT_SHRT("channel-id", 'c', &cfg.chid, ch),
+ OPT_SHRT("lun-id", 'l', &cfg.lunid, lun),
+ OPT_SHRT("plane-id", 'p', &cfg.plnid, pln),
+ OPT_SHRT("block-id", 'b', &cfg.blkid, blk),
+ OPT_SHRT("value", 'v', &cfg.value, value),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ printf("Updating: Ch.: %u LUN: %u Plane: %u Block: %u -> %u\n",
+ cfg.chid, cfg.lunid, cfg.plnid, cfg.blkid, cfg.value);
+ return lnvm_do_set_bbtbl(fd, cfg.namespace_id, cfg.chid, cfg.lunid,
+ cfg.plnid, cfg.blkid, cfg.value);
+}
diff --git a/plugins/lnvm/lnvm-nvme.h b/plugins/lnvm/lnvm-nvme.h
new file mode 100644
index 0000000..45b3cf0
--- /dev/null
+++ b/plugins/lnvm/lnvm-nvme.h
@@ -0,0 +1,27 @@
+
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/lnvm/lnvm-nvme
+
+#if !defined(LNVM_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define LNVM_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("lnvm", "LightNVM specific extensions"),
+ COMMAND_LIST(
+ ENTRY("list", "List available LightNVM devices", lnvm_list)
+ ENTRY("info", "List general information and available target engines", lnvm_info)
+ ENTRY("id-ns", "List geometry for LightNVM device", lnvm_id_ns, "geometry")
+ ENTRY("chunk-log", "Chunk Information Log Page", lnvm_chunk_log)
+ ENTRY("init", "Initialize media manager on LightNVM device", lnvm_init)
+ ENTRY("create", "Create target on top of a LightNVM device", lnvm_create_tgt)
+ ENTRY("remove", "Remove target from device", lnvm_remove_tgt)
+ ENTRY("factory", "Reset device to factory state", lnvm_factory_init)
+ ENTRY("diag-bbtbl", "Diagnose bad block table", lnvm_get_bbtbl)
+ ENTRY("diag-set-bbtbl", "Update bad block table", lnvm_set_bbtbl)
+ )
+);
+
+#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..c75f49c
--- /dev/null
+++ b/plugins/memblaze/memblaze-nvme.c
@@ -0,0 +1,759 @@
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "linux/nvme_ioctl.h"
+
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+
+#include "argconfig.h"
+#include "suffix.h"
+
+#define CREATE_CMD
+#include "memblaze-nvme.h"
+#include "memblaze-utils.h"
+
+enum {
+ MB_FEAT_POWER_MGMT = 0xc6,
+};
+
+/*
+ * 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.83 = raisin
+#define IS_RAISIN(str) (!strcmp(str, "2.83"))
+// 2.13 = papaya
+#define IS_PAPAYA(str) (!strcmp(str, "2.13"))
+#define STR_VER_SIZE 5
+
+int getlogpage_format_type(char *fw_ver)
+{
+ char fw_ver_local[STR_VER_SIZE];
+ strncpy(fw_ver_local, fw_ver, STR_VER_SIZE);
+ *(fw_ver_local + STR_VER_SIZE - 1) = '\0';
+ if ( IS_RAISIN(fw_ver_local) )
+ {
+ return INTEL_FORMAT;
+ }
+ else
+ {
+ return MEMBLAZE_FORMAT;
+ }
+}
+
+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);
+}
+
+#define STRN2_01 "Additional Smart Log for NVME device"
+#define STRN2_02 "namespace-id"
+#define STRN1_01 "key"
+#define STRN1_02 "normalized"
+#define STRN1_03 "raw"
+#define STR00_01 "program_fail_count"
+#define STR01_01 "erase_fail_count"
+#define STR02_01 "wear_leveling"
+#define STR02_03 "min: "
+#define STR02_04 ", max: "
+#define STR02_05 ", avg: "
+#define STR03_01 "end_to_end_error_detection_count"
+#define STR04_01 "crc_error_count"
+#define STR05_01 "timed_workload_media_wear"
+#define STR06_01 "timed_workload_host_reads"
+#define STR07_01 "timed_workload_timer"
+#define STR07_02 " min"
+#define STR08_01 "thermal_throttle_status"
+#define STR08_02 ", cnt: "
+#define STR09_01 "retry_buffer_overflow_count"
+#define STR10_01 "pll_lock_loss_count"
+#define STR11_01 "nand_bytes_written"
+#define STR11_03 "sectors: "
+#define STR12_01 "host_bytes_written"
+#define STR12_03 "sectors: "
+#define STR13_01 "system_area_life_left"
+#define STR14_01 "total_read"
+#define STR15_01 "tempt_since_born"
+#define STR15_03 "max: "
+#define STR15_04 ", min: "
+#define STR15_05 ", curr: "
+#define STR16_01 "power_consumption"
+#define STR16_03 "max: "
+#define STR16_04 ", min: "
+#define STR16_05 ", curr: "
+#define STR17_01 "tempt_since_bootup"
+#define STR17_03 "max: "
+#define STR17_04 ", min: "
+#define STR17_05 ", curr: "
+#define STR18_01 "power_loss_protection"
+#define STR19_01 "read_fail"
+#define STR20_01 "thermal_throttle_time"
+#define STR21_01 "flash_media_error"
+
+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));
+
+ /* Table Title */
+ printf("%s:%s %s:%x\n", STRN2_01, devname, STRN2_02, nsid);
+ /* Clumn Name*/
+ printf("%-34s%-11s%s\n", STRN1_01, STRN1_02, STRN1_03);
+ /* 00 RAISIN_SI_VD_PROGRAM_FAIL */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PROGRAM_FAIL, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR00_01, *nm, int48_to_long(raw));
+ /* 01 RAISIN_SI_VD_ERASE_FAIL */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_ERASE_FAIL, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR01_01, *nm, int48_to_long(raw));
+ /* 02 RAISIN_SI_VD_WEARLEVELING_COUNT */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_WEARLEVELING_COUNT, nm, raw);
+ printf("%-31s : %3d%% %s%u%s%u%s%u\n", STR02_01, *nm,
+ STR02_03, *raw, STR02_04, *(raw+2), STR02_05, *(raw+4));
+ /* 03 RAISIN_SI_VD_E2E_DECTECTION_COUNT */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_E2E_DECTECTION_COUNT, nm, raw);
+ printf("%-31s: %3d%% %"PRIu64"\n", STR03_01, *nm, int48_to_long(raw));
+ /* 04 RAISIN_SI_VD_PCIE_CRC_ERR_COUNT */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PCIE_CRC_ERR_COUNT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR04_01, *nm, int48_to_long(raw));
+ /* 05 RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_MEDIA_WEAR, nm, raw);
+ printf("%-32s: %3d%% %.3f%%\n", STR05_01, *nm, ((float)int48_to_long(raw))/1000);
+ /* 06 RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_HOST_READ, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"%%\n", STR06_01, *nm, int48_to_long(raw));
+ /* 07 RAISIN_SI_VD_TIMED_WORKLOAD_TIMER */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TIMED_WORKLOAD_TIMER, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"%s\n", STR07_01, *nm, int48_to_long(raw), STR07_02);
+ /* 08 RAISIN_SI_VD_THERMAL_THROTTLE_STATUS */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_THERMAL_THROTTLE_STATUS, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"%%%s%"PRIu64"\n", STR08_01, *nm,
+ int48_to_long(raw), STR08_02, int48_to_long(raw+1));
+ /* 09 RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_RETRY_BUFF_OVERFLOW_COUNT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR09_01, *nm, int48_to_long(raw));
+ /* 10 RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_PLL_LOCK_LOSS_COUNT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR10_01, *nm, int48_to_long(raw));
+ /* 11 RAISIN_SI_VD_TOTAL_WRITE */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_WRITE, nm, raw);
+ printf("%-32s: %3d%% %s%"PRIu64"\n", STR11_01, *nm, STR11_03, int48_to_long(raw));
+ /* 12 RAISIN_SI_VD_HOST_WRITE */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_HOST_WRITE, nm, raw);
+ printf("%-32s: %3d%% %s%"PRIu64"\n", STR12_01, *nm, STR12_03, int48_to_long(raw));
+ /* 13 RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_SYSTEM_AREA_LIFE_LEFT, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR13_01, *nm, int48_to_long(raw));
+ /* 14 RAISIN_SI_VD_TOTAL_READ */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TOTAL_READ, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR14_01, *nm, int48_to_long(raw));
+ /* 15 RAISIN_SI_VD_TEMPT_SINCE_BORN */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TEMPT_SINCE_BORN, nm, raw);
+ printf("%-32s: %3d%% %s%u%s%u%s%u\n", STR15_01, *nm,
+ STR15_03, *raw, STR15_04, *(raw+2), STR15_05, *(raw+4));
+ /* 16 RAISIN_SI_VD_POWER_CONSUMPTION */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_POWER_CONSUMPTION, nm, raw);
+ printf("%-32s: %3d%% %s%u%s%u%s%u\n", STR16_01, *nm,
+ STR16_03, *raw, STR16_04, *(raw+2), STR16_05, *(raw+4));
+ /* 17 RAISIN_SI_VD_TEMPT_SINCE_BOOTUP */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_TEMPT_SINCE_BOOTUP, nm, raw);
+ printf("%-32s: %3d%% %s%u%s%u%s%u\n", STR17_01, *nm, STR17_03, *raw,
+ STR17_04, *(raw+2), STR17_05, *(raw+4));
+ /* 18 RAISIN_SI_VD_POWER_LOSS_PROTECTION */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_POWER_LOSS_PROTECTION, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR18_01, *nm, int48_to_long(raw));
+ /* 19 RAISIN_SI_VD_READ_FAIL */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_READ_FAIL, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR19_01, *nm, int48_to_long(raw));
+ /* 20 RAISIN_SI_VD_THERMAL_THROTTLE_TIME */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_THERMAL_THROTTLE_TIME, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR20_01, *nm, int48_to_long(raw));
+ /* 21 RAISIN_SI_VD_FLASH_MEDIA_ERROR */
+ get_memblaze_new_smart_info(smart, RAISIN_SI_VD_FLASH_MEDIA_ERROR, nm, raw);
+ printf("%-32s: %3d%% %"PRIu64"\n", STR21_01, *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];
+ struct nvme_memblaze_smart_log_item *item;
+
+ strncpy(fw_ver_local, fw_ver, STR_VER_SIZE);
+ *(fw_ver_local + STR_VER_SIZE - 1) = '\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));
+
+ /* 00 RAISIN_SI_VD_PROGRAM_FAIL */
+ get_memblaze_new_smart_info(s, PROGRAM_FAIL, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ STR00_01, *nm, int48_to_long(raw));
+ /* 01 RAISIN_SI_VD_ERASE_FAIL */
+ get_memblaze_new_smart_info(s, ERASE_FAIL, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ STR01_01, *nm, int48_to_long(raw));
+ /* 02 RAISIN_SI_VD_WEARLEVELING_COUNT */
+ get_memblaze_new_smart_info(s, WEARLEVELING_COUNT, nm, raw);
+ printf("%-31s : %3d%% %s%u%s%u%s%u\n",
+ STR02_01, *nm, STR02_03, *raw, STR02_04, *(raw+2), STR02_05, *(raw+4));
+ /* 11 RAISIN_SI_VD_TOTAL_WRITE */
+ get_memblaze_new_smart_info(s, TOTAL_WRITE, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ STR11_01, *nm, 32*int48_to_long(raw));
+ /* 12 RAISIN_SI_VD_HOST_WRITE */
+ get_memblaze_new_smart_info(s, HOST_WRITE, nm, raw);
+ printf("%-32s : %3d%% %"PRIu64"\n",
+ STR12_01, *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(fw_ver)) // 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;
+}
+
+static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_memblaze_smart_log smart_log;
+ int err, fd;
+ 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 config {
+ __u32 namespace_id;
+ int raw_binary;
+ };
+
+ struct config cfg = {
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ err = nvme_get_log(fd, cfg.namespace_id, 0xca, false,
+ sizeof(smart_log), &smart_log);
+ if (!err) {
+ if (!cfg.raw_binary)
+ err = show_memblaze_smart_log(fd, cfg.namespace_id, devicename, &smart_log);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err);
+
+ return err;
+}
+
+static char *mb_feature_to_string(int feature)
+{
+ switch (feature) {
+ case MB_FEAT_POWER_MGMT: return "Memblaze power management";
+ default: return "Unknown";
+ }
+}
+
+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 "\
+ "behaviour of the feature. Each Feature has three possible "\
+ "settings: default, saveable, and current. If a Feature is "\
+ "saveable, it may be modified by set-feature. Default values "\
+ "are vendor-specific and not changeable. Use set-feature to "\
+ "change saveable Features.\n\n"\
+ "Available additional feature id:\n"\
+ "0xc6: Memblaze power management\n"\
+ " (value 0 - 25w, 1 - 20w, 2 - 15w)";
+ const char *raw = "show feature in binary format";
+ const char *namespace_id = "identifier of desired namespace";
+ const char *feature_id = "hexadecimal feature name";
+ const char *sel = "[0-3]: curr./default/saved/supp.";
+ const char *data_len = "buffer len (if) data is returned";
+ const char *cdw11 = "dword 11 for interrupt vector config";
+ const char *human_readable = "show infos in readable format";
+ int err, fd;
+ __u32 result;
+ void *buf = NULL;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 feature_id;
+ __u8 sel;
+ __u32 cdw11;
+ __u32 data_len;
+ int raw_binary;
+ int human_readable;
+ };
+
+ 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_UINT("cdw11", 'c', &cfg.cdw11, cdw11),
+ OPT_FLAG("human-readable", 'H', &cfg.human_readable, human_readable),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (cfg.sel > 7) {
+ fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel);
+ return EINVAL;
+ }
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ return EINVAL;
+ }
+ if (cfg.data_len) {
+ if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+ exit(ENOMEM);
+ memset(buf, 0, cfg.data_len);
+ }
+
+ err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11,
+ cfg.data_len, buf, &result);
+ if (!err) {
+ printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id,
+ mb_feature_to_string(cfg.feature_id),
+ nvme_select_to_string(cfg.sel), result);
+ if (cfg.human_readable)
+ nvme_feature_show_fields(cfg.feature_id, result, buf);
+ else {
+ if (buf) {
+ if (!cfg.raw_binary)
+ d(buf, cfg.data_len, 16, 1);
+ else
+ d_raw(buf, cfg.data_len);
+ }
+ }
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ if (buf)
+ free(buf);
+ return err;
+}
+
+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"\
+ "0xc6: Memblaze power management\n"\
+ " (value 0 - 25w, 1 - 20w, 2 - 15w)";
+ const char *namespace_id = "desired namespace";
+ const char *feature_id = "hex feature name (required)";
+ const char *data_len = "buffer length if data required";
+ const char *data = "optional file for feature data (default stdin)";
+ const char *value = "new value of feature (required)";
+ const char *save = "specifies that the controller shall save the attribute";
+ int err, fd;
+ __u32 result;
+ void *buf = NULL;
+ int ffd = STDIN_FILENO;
+
+ struct config {
+ char *file;
+ __u32 namespace_id;
+ __u32 feature_id;
+ __u32 value;
+ __u32 data_len;
+ int 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ return EINVAL;
+ }
+
+ if (cfg.data_len) {
+ if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+ exit(ENOMEM);
+ memset(buf, 0, cfg.data_len);
+ }
+
+ if (buf) {
+ if (strlen(cfg.file)) {
+ ffd = open(cfg.file, O_RDONLY);
+ if (ffd <= 0) {
+ fprintf(stderr, "no firmware file provided\n");
+ err = EINVAL;
+ goto free;
+ }
+ }
+ if (read(ffd, (void *)buf, cfg.data_len) < 0) {
+ fprintf(stderr, "failed to read data buffer from input file\n");
+ err = EINVAL;
+ goto free;
+ }
+ }
+
+ err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value,
+ 0, cfg.save, cfg.data_len, buf, &result);
+ if (err < 0) {
+ perror("set-feature");
+ goto free;
+ }
+ if (!err) {
+ printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
+ mb_feature_to_string(cfg.feature_id), cfg.value);
+ if (buf)
+ d(buf, cfg.data_len, 16, 1);
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+
+free:
+ if (buf)
+ free(buf);
+ return err;
+}
+
+static int memblaze_fw_commit(int fd, int select)
+{
+ struct nvme_admin_cmd cmd = {
+ .opcode = nvme_admin_activate_fw,
+ .cdw10 = 8,
+ .cdw12 = select,
+ };
+
+ return nvme_submit_admin_passthru(fd, &cmd);
+}
+
+static int memblaze_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 fd, selectNo,fw_fd,fw_size,err,offset = 0;
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (strlen(cfg.select) != 3) {
+ fprintf(stderr, "Invalid select flag\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < 3; i++) {
+ cfg.select[i] = toupper(cfg.select[i]);
+ }
+
+ if (strncmp(cfg.select,"OOB", 3) == 0) {
+ selectNo = 18;
+ } else if (strncmp(cfg.select,"EEP", 3) == 0) {
+ selectNo = 10;
+ } else if (strncmp(cfg.select,"ALL", 3) == 0) {
+ selectNo = 26;
+ } else {
+ fprintf(stderr, "Invalid select flag\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ fw_fd = open(cfg.fw, O_RDONLY);
+ if (fw_fd < 0) {
+ fprintf(stderr, "no firmware file provided\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ err = fstat(fw_fd, &sb);
+ if (err < 0) {
+ perror("fstat");
+ err = errno;
+ }
+
+ fw_size = sb.st_size;
+ if (fw_size & 0x3) {
+ fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size);
+ err = EINVAL;
+ goto out;
+ }
+
+ if (posix_memalign(&fw_buf, getpagesize(), fw_size)) {
+ fprintf(stderr, "No memory for f/w size:%d\n", fw_size);
+ err = ENOMEM;
+ goto out;
+ }
+
+ if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size)))
+ return EIO;
+
+ while (fw_size > 0) {
+ xfer = min(xfer, fw_size);
+
+ err = nvme_fw_download(fd, offset, xfer, fw_buf);
+ if (err < 0) {
+ perror("fw-download");
+ goto out;
+ } else if (err != 0) {
+ fprintf(stderr, "NVME Admin command error:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ goto out;
+ }
+ fw_buf += xfer;
+ fw_size -= xfer;
+ offset += xfer;
+ }
+
+ err = memblaze_fw_commit(fd,selectNo);
+
+ if(err == 0x10B || err == 0x20B) {
+ err = 0;
+ fprintf(stderr, "Update successful! Please power cycle for changes to take effect\n");
+ }
+
+out:
+ return err;
+}
+
diff --git a/plugins/memblaze/memblaze-nvme.h b/plugins/memblaze/memblaze-nvme.h
new file mode 100644
index 0000000..8f7024d
--- /dev/null
+++ b/plugins/memblaze/memblaze-nvme.h
@@ -0,0 +1,27 @@
+#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"
+#include "common.h"
+
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+PLUGIN(NAME("memblaze", "Memblaze vendor specific extensions"),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve Memblaze SMART Log, show it", get_additional_smart_log)
+ ENTRY("get-feature-add", "Get Memblaze feature and show the resulting value", get_additional_feature)
+ ENTRY("set-feature-add", "Set a Memblaze feature and show the resulting value", set_additional_feature)
+ ENTRY("select-download", "Selective Firmware Download", memblaze_selective_download)
+ )
+);
+
+#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..a67ee5e
--- /dev/null
+++ b/plugins/memblaze/memblaze-utils.h
@@ -0,0 +1,164 @@
+#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
+
+typedef unsigned char u8;
+
+// 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];
+};
+
+#endif // __MEMBLAZE_UTILS_H__
+
diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c
new file mode 100644
index 0000000..165fcf0
--- /dev/null
+++ b/plugins/micron/micron-nvme.c
@@ -0,0 +1,1180 @@
+#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 "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include <sys/ioctl.h>
+#include <limits.h>
+
+#define CREATE_CMD
+#include "micron-nvme.h"
+#define min(x, y) ((x) > (y) ? (y) : (x))
+#define SensorCount 2
+#define C5_log_size (((452 + 16 * 1024) / 4) * 4096)
+#define D0_log_size 256
+#define MaxLogChunk 16 * 1024
+#define CommonChunkSize 16 * 4096
+
+typedef struct _LogPageHeader_t {
+ unsigned char numDwordsInLogPageHeaderLo;
+ unsigned char logPageHeaderFormatVersion;
+ unsigned char logPageId;
+ unsigned char numDwordsInLogPageHeaderHi;
+ unsigned int numValidDwordsInPayload;
+ unsigned int numDwordsInEntireLogPage;
+} LogPageHeader_t;
+
+/*
+ * Useful Helper functions
+ */
+
+static int micron_fw_commit(int fd, int select)
+{
+ struct nvme_admin_cmd cmd = {
+ .opcode = nvme_admin_activate_fw,
+ .cdw10 = 8,
+ .cdw12 = select,
+ };
+ return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+
+}
+
+static int ZipAndRemoveDir(char *strDirName, char *strFileName)
+{
+ int err = 0;
+ char strBuffer[PATH_MAX];
+ int nRet;
+
+ sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName,
+ strDirName);
+
+ nRet = system(strBuffer);
+
+ if (nRet < 0) {
+ printf("Unable to create zip package!\n");
+ err = EINVAL;
+ goto exit_status;
+ }
+
+ sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName);
+ nRet = system(strBuffer);
+ if (nRet < 0) {
+ printf("Unable to remove temporary files!\n");
+ err = EINVAL;
+ goto exit_status;
+ }
+
+ exit_status:
+ err = system("rm -f temp.txt");
+ return err;
+}
+
+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;
+ }
+
+ fileLocation = (char *)malloc(length + 1);
+ strncpy(fileLocation, strFilePath, length);
+ fileLocation[length] = '\0';
+
+ while (fileLocation[k] != '\0') {
+ if (fileLocation[k] == '\\') {
+ fileLocation[k] = '/';
+ }
+ }
+
+ length = (int)strlen(fileLocation);
+
+ if (':' == fileLocation[length - 1]) {
+ strTemp = (char *)malloc(length + 2);
+ strcpy(strTemp, fileLocation);
+ strcat(strTemp, "/");
+ free(fileLocation);
+
+ length = (int)strlen(strTemp);
+ fileLocation = (char *)malloc(length + 1);
+ memcpy(fileLocation, strTemp, length + 1);
+ free(strTemp);
+ }
+
+ if (stat(fileLocation, &st) != 0) {
+ free(fileLocation);
+ goto exit_status;
+ }
+ free(fileLocation);
+ } else {
+ goto exit_status;
+ }
+ }
+
+ nIndex = 0;
+ for (i = 0; i < (int)strlen(strSN); i++) {
+ if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') {
+ strMainDirName[nIndex++] = strSN[i];
+ }
+ }
+ strMainDirName[nIndex] = '\0';
+
+ j = 1;
+ while (stat(strMainDirName, &dirStat) == 0) {
+ strMainDirName[nIndex] = '\0';
+ sprintf(strAppend, "-%d", j);
+ strcat(strMainDirName, strAppend);
+ j++;
+ }
+
+ mkdir(strMainDirName, 0777);
+
+ if (strOSDirName != NULL) {
+ sprintf(strOSDirName, "%s/%s", strMainDirName, "OS");
+ mkdir(strOSDirName, 0777);
+
+ }
+ if (strCtrlDirName != NULL) {
+ sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller");
+ mkdir(strCtrlDirName, 0777);
+
+ }
+
+ exit_status:
+ return err;
+}
+
+static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize)
+{
+ int err = 0;
+ struct nvme_admin_cmd cmd;
+ unsigned int uiXferDwords = 0;
+ unsigned char pTmpBuf[CommonChunkSize] = { 0 };
+ LogPageHeader_t *pLogHeader = NULL;
+
+ if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) {
+ cmd.opcode = 0x02;
+ cmd.cdw10 = ucLogID;
+ uiXferDwords = (unsigned int)(CommonChunkSize / 4);
+ cmd.nsid = 0xFFFFFFFF;
+ cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16;
+ cmd.data_len = CommonChunkSize;
+ cmd.addr = (__u64) (uintptr_t) & pTmpBuf;
+ err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd);
+ if (err == 0) {
+ pLogHeader = (LogPageHeader_t *) pTmpBuf;
+ LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader;
+ *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4;
+ } else {
+ printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err);
+ *nLogSize = 0;
+ }
+ }
+ return err;
+}
+
+static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize)
+{
+ int err = 0;
+ struct nvme_admin_cmd cmd = { 0 };
+ unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int);
+ unsigned int uiMaxChunk = uiNumDwords;
+ unsigned int uiNumChunks = 1;
+ unsigned int uiXferDwords = 0;
+ unsigned long long ullBytesRead = 0;
+ unsigned char *pTempPtr = pBuffer;
+ unsigned char ucOpCode = 0x02;
+
+ if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) {
+ uiMaxChunk = 4096;
+ } else if (uiMaxChunk > 16 * 1024) {
+ uiMaxChunk = 16 * 1024;
+ }
+
+ uiNumChunks = uiNumDwords / uiMaxChunk;
+ if (uiNumDwords % uiMaxChunk > 0) {
+ uiNumChunks += 1;
+ }
+
+ for (unsigned int i = 0; i < uiNumChunks; i++) {
+ memset(&cmd, 0, sizeof(cmd));
+ uiXferDwords = uiMaxChunk;
+ if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) {
+ uiXferDwords = uiNumDwords % uiMaxChunk;
+ }
+
+ cmd.opcode = ucOpCode;
+ cmd.cdw10 |= ucLogID;
+ cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16;
+
+ if (ucLogID == 0x7) {
+ cmd.cdw10 |= 0x80;
+ }
+ if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) {
+ cmd.cdw11 = 1;
+ }
+ if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) {
+ unsigned long long ullOffset = ullBytesRead;
+ cmd.cdw12 = ullOffset & 0xFFFFFFFF;
+ cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF;
+ }
+
+ cmd.addr = (__u64) (uintptr_t) pTempPtr;
+ cmd.nsid = 0xFFFFFFFF;
+ cmd.data_len = uiXferDwords * 4;
+ err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd);
+ ullBytesRead += uiXferDwords * 4;
+ pTempPtr = pBuffer + ullBytesRead;
+ }
+
+ return err;
+}
+
+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)
+ 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)
+{
+ struct nvme_admin_cmd cmd;
+ int err = 0;
+ unsigned char pTmpBuf[CommonChunkSize] = { 0 };
+ unsigned int uiMaxChunk = 0;
+ unsigned int uiXferDwords = 0;
+ int nBytesRead = 0;
+ unsigned char *pTempPtr = NULL;
+
+ uiMaxChunk = CommonChunkSize / 4;
+ pTempPtr = (unsigned char *)malloc(nBuffSize);
+ if (!pTempPtr) {
+ goto exit_status;
+ }
+ memset(pTempPtr, 0, nBuffSize);
+
+ while (nBytesRead < nBuffSize) {
+ int nBytesRemaining = nBuffSize - nBytesRead;
+
+ memset(pTmpBuf, 0, CommonChunkSize);
+
+ uiXferDwords = uiMaxChunk;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.opcode = 0x02;
+ cmd.cdw10 |= ucLogID;
+ cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16;
+
+ if (nBytesRead > 0) {
+ unsigned long long ullOffset = (unsigned long long)nBytesRead;
+ cmd.cdw12 = ullOffset & 0xFFFFFFFF;
+ cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF;
+ }
+ cmd.nsid = 0xFFFFFFFF;
+ cmd.data_len = uiXferDwords * 4;
+ cmd.addr = (__u64) (uintptr_t) pTmpBuf;
+
+ err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd);
+
+ if (nBytesRemaining >= (int)(uiMaxChunk * 4)) {
+ memcpy(&pTempPtr[nBytesRead], pTmpBuf, uiMaxChunk * 4);
+ } else {
+ memcpy(&pTempPtr[nBytesRead], pTmpBuf, nBytesRemaining);
+ }
+
+ nBytesRead += (int)uiXferDwords *4;
+ }
+ *pBuffer = pTempPtr;
+
+ exit_status:
+ return err;
+}
+
+/*
+ * Plugin Commands
+ */
+
+static int micron_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ const char *desc =
+ "This performs a selective firmware download, which allows the user to "
+ "select which firmware binary to update for 9200 devices. This requires a power cycle once the "
+ "update completes. The options available are: \n\n"
+ "OOB - This updates the OOB and main firmware\n"
+ "EEP - This updates the eeprom and main firmware\n"
+ "ALL - This updates the eeprom, OOB, and main firmware";
+ const char *fw = "firmware file (required)";
+ const char *select = "FW Select (e.g., --select=ALL)";
+ int xfer = 4096;
+ void *fw_buf;
+ int fd, selectNo, fw_fd, fw_size, err, offset = 0;
+ struct stat sb;
+
+ struct config {
+ char *fw;
+ char *select;
+ };
+
+ struct config cfg = {
+ .fw = "",
+ .select = "\0",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw),
+ OPT_STRING("select", 's', "flag", &cfg.select, select),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (fd < 0)
+ return fd;
+
+ if (strlen(cfg.select) != 3) {
+ fprintf(stderr, "Invalid select flag\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ cfg.select[i] = toupper(cfg.select[i]);
+ }
+
+ if (strncmp(cfg.select, "OOB", 3) == 0) {
+ selectNo = 18;
+ } else if (strncmp(cfg.select, "EEP", 3) == 0) {
+ selectNo = 10;
+ } else if (strncmp(cfg.select, "ALL", 3) == 0) {
+ selectNo = 26;
+ } else {
+ fprintf(stderr, "Invalid select flag\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ fw_fd = open(cfg.fw, O_RDONLY);
+ if (fw_fd < 0) {
+ fprintf(stderr, "no firmware file provided\n");
+ err = EINVAL;
+ goto out;
+ }
+
+ err = fstat(fw_fd, &sb);
+ if (err < 0) {
+ perror("fstat");
+ err = errno;
+ }
+
+ fw_size = sb.st_size;
+ if (fw_size & 0x3) {
+ fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size);
+ err = EINVAL;
+ goto out;
+ }
+
+ if (posix_memalign(&fw_buf, getpagesize(), fw_size)) {
+ fprintf(stderr, "No memory for f/w size:%d\n", fw_size);
+ err = ENOMEM;
+ goto out;
+ }
+
+ if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size)))
+ return EIO;
+
+ while (fw_size > 0) {
+ xfer = min(xfer, fw_size);
+
+ err = nvme_fw_download(fd, offset, xfer, fw_buf);
+ if (err < 0) {
+ perror("fw-download");
+ goto out;
+ } else if (err != 0) {
+ fprintf(stderr, "NVME Admin command error:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ goto out;
+ }
+ fw_buf += xfer;
+ fw_size -= xfer;
+ offset += xfer;
+ }
+
+ err = micron_fw_commit(fd, selectNo);
+
+ if (err == 0x10B || err == 0x20B) {
+ err = 0;
+ fprintf(stderr,
+ "Update successful! Please power cycle for changes to take effect\n");
+ }
+
+ out:
+ 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, fd = 0, err = 0;
+ unsigned int tempSensors[SensorCount] = { 0 };
+ const char *desc = "Retrieve Micron temperature info for the given device ";
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+
+ err = nvme_smart_log(fd, 0xffffffff, &smart_log);
+ if (!err) {
+ printf("Micron temperature information:\n");
+ temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]);
+ temperature = temperature ? temperature - 273 : 0;
+ for (i = 0; i < SensorCount; i++) {
+ tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]);
+ tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0;
+ }
+ printf("%-10s : %u C\n", "Current Composite Temperature", temperature);
+ for (i = 0; i < SensorCount; i++) {
+ printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]);
+ }
+ }
+ return err;
+}
+
+static int micron_pcie_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int err = 0, bus = 0, domain = 0, device = 0, function = 0;
+ char strTempFile[1024], strTempFile2[1024], command[1024];
+ char *businfo = NULL;
+ char *devicename = NULL;
+ char tdevice[NAME_MAX] = { 0 };
+ ssize_t sLinkSize = 0;
+ FILE *fp;
+ char correctable[8] = { 0 };
+ char uncorrectable[8] = { 0 };
+ char *res;
+
+ if (argc != 2) {
+ printf("vs-pcie-stats: Invalid argument\n");
+ printf("Usage: nvme micron vs-pcie-stats <device>\n\n");
+ goto out;
+ }
+ if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) {
+ devicename = strrchr(argv[optind], '/');
+ } else if (strstr(argv[optind], "/dev/nvme")) {
+ devicename = strrchr(argv[optind], '/');
+ sprintf(tdevice, "%s%s", devicename, "n1");
+ devicename = tdevice;
+ } else {
+ printf("Invalid device specified!\n");
+ goto out;
+ }
+ sprintf(strTempFile, "/sys/block/%s/device", devicename);
+ sLinkSize = readlink(strTempFile, strTempFile2, 1024);
+ if (sLinkSize < 0) {
+ printf("Unable to read device\n");
+ goto out;
+ }
+ if (strstr(strTempFile2, "../../nvme")) {
+ sprintf(strTempFile, "/sys/block/%s/device/device", devicename);
+ sLinkSize = readlink(strTempFile, strTempFile2, 1024);
+ if (sLinkSize < 0) {
+ printf("Unable to read device\n");
+ goto out;
+ }
+ }
+ businfo = strrchr(strTempFile2, '/');
+ sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function);
+ sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device,
+ function);
+ fp = popen(command, "r");
+ if (fp == NULL) {
+ printf("Unable to retrieve error count\n");
+ goto out;
+ }
+ res = fgets(correctable, sizeof(correctable), fp);
+ if (res == NULL) {
+ printf("Unable to retrieve error count\n");
+ goto out;
+ }
+ pclose(fp);
+
+ sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device,
+ function);
+ fp = popen(command, "r");
+ if (fp == NULL) {
+ printf("Unable to retrieve error count\n");
+ goto out;
+ }
+ res = fgets(uncorrectable, sizeof(uncorrectable), fp);
+ if (res == NULL) {
+ printf("Unable to retrieve error count\n");
+ goto out;
+ }
+ pclose(fp);
+ printf("PCIE Stats:\n");
+ printf("Device correctable errors detected: %s\n", correctable);
+ printf("Device uncorrectable errors detected: %s\n", uncorrectable);
+
+ out:
+ return err;
+}
+
+static int micron_clear_pcie_correctable_errors(int argc, char **argv,
+ struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = 0, bus = 0, domain = 0, device = 0, function = 0;
+ char strTempFile[1024], strTempFile2[1024], command[1024];
+ char *businfo = NULL;
+ char *devicename = NULL;
+ char tdevice[PATH_MAX] = { 0 };
+ ssize_t sLinkSize = 0;
+ FILE *fp;
+ char correctable[8] = { 0 };
+ char *res;
+
+ if (argc != 2) {
+ printf("clear-pcie-correctable-errors: Invalid argument\n");
+ printf ("Usage: nvme micron clear-pcie-correctable-errors <device>\n\n");
+ goto out;
+ }
+ if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) {
+ devicename = strrchr(argv[optind], '/');
+ } else if (strstr(argv[optind], "/dev/nvme")) {
+ devicename = strrchr(argv[optind], '/');
+ sprintf(tdevice, "%s%s", devicename, "n1");
+ devicename = tdevice;
+ } else {
+ printf("Invalid device specified!\n");
+ goto out;
+ }
+ err = snprintf(strTempFile, sizeof(strTempFile),
+ "/sys/block/%s/device", devicename);
+ if (err < 0)
+ goto out;
+ sLinkSize = readlink(strTempFile, strTempFile2, 1024);
+ if (sLinkSize < 0) {
+ printf("Unable to read device\n");
+ goto out;
+ }
+ if (strstr(strTempFile2, "../../nvme")) {
+ err = snprintf(strTempFile, sizeof(strTempFile),
+ "/sys/block/%s/device/device", devicename);
+ if (err < 0)
+ goto out;
+ sLinkSize = readlink(strTempFile, strTempFile2, 1024);
+ if (sLinkSize < 0) {
+ printf("Unable to read device\n");
+ goto out;
+ }
+ }
+ businfo = strrchr(strTempFile2, '/');
+ sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function);
+ sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus,
+ device, function);
+
+ fp = popen(command, "r");
+ if (fp == NULL) {
+ printf("Unable to clear error count\n");
+ goto out;
+ }
+ pclose(fp);
+
+ sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device,
+ function);
+ fp = popen(command, "r");
+ if (fp == NULL) {
+ printf("Unable to retrieve error count\n");
+ goto out;
+ }
+ res = fgets(correctable, sizeof(correctable), fp);
+ if (res == NULL) {
+ printf("Unable to retrieve error count\n");
+ goto out;
+ }
+ pclose(fp);
+ printf("Device correctable errors cleared!\n");
+ printf("Device correctable errors detected: %s\n", correctable);
+
+ out:
+ return err;
+}
+
+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[64] = { 0 };
+ struct nvme_id_ctrl ctrl;
+ int fd, err;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+
+ err = nvme_identify_ctrl(fd, &ctrl);
+ if (err)
+ goto out;
+
+ err = NVMEGetLogPage(fd, 0xD0, (unsigned char *)extSmartLog, D0_log_size);
+ if (err)
+ goto out;
+
+ unsigned long long count = ((unsigned long long)extSmartLog[45] << 32) | extSmartLog[44];
+ printf("%-40s : 0x%llx\n", "NAND Writes (Bytes Written)", count);
+ printf("%-40s : ", "Program Failure Count");
+
+ unsigned long long count_hi = ((unsigned long long)extSmartLog[39] << 32) | extSmartLog[38];
+ unsigned long long count_lo = ((unsigned long long)extSmartLog[37] << 32) | extSmartLog[36];
+ if (count_hi != 0)
+ printf("0x%llx%016llx", count_hi, count_lo);
+ else
+ printf("0x%llx\n", count_lo);
+
+ count = ((unsigned long long)extSmartLog[25] << 32) | extSmartLog[24];
+ printf("%-40s : 0x%llx\n", "Erase Failures", count);
+ printf("%-40s : 0x%x\n", "Bad Block Count", extSmartLog[3]);
+
+ count = (unsigned long long)extSmartLog[3] - (count_lo + count);
+ printf("%-40s : 0x%llx\n", "NAND XOR/RAID Recovery Trigger Events", count);
+ printf("%-40s : 0x%x\n", "NSZE Change Supported", (ctrl.oacs >> 3) & 0x1);
+ printf("%-40s : 0x%x\n", "Number of NSZE Modifications", extSmartLog[1]);
+ out:
+ close(fd);
+ return err;
+}
+
+typedef enum { M5410 = 0, M51AX, M51BX, UNKNOWN_MODEL } eDriveModel;
+
+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;
+
+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) {
+ ret = read(fd, idstr, sizeof(idstr));
+ close(fd);
+ }
+
+ if (fd < 0 || ret < 0)
+ perror(file);
+ else
+ *id = strtol(idstr, NULL, 16);
+
+ return ret;
+}
+
+static eDriveModel GetDriveModel(int idx)
+{
+ eDriveModel eModel = UNKNOWN_MODEL;
+ char path[512];
+
+ sprintf(path, fvendorid1, idx);
+ if (ReadSysFile(path, &vendor_id) < 0) {
+ sprintf(path, fvendorid2, idx);
+ ReadSysFile(path, &vendor_id);
+ }
+ sprintf(path, fdeviceid1, idx);
+ if (ReadSysFile(path, &device_id) < 0) {
+ sprintf(path, fdeviceid2, idx);
+ ReadSysFile(path, &device_id);
+ }
+
+ if (vendor_id == 0x1344) {
+ switch (device_id) {
+ case 0x5410:
+ eModel = M5410;
+ break;
+ case 0x51A0:
+ case 0x51A1:
+ case 0x51A2:
+ eModel = M51AX;
+ break;
+ case 0x51B0:
+ case 0x51B1:
+ case 0x51B2:
+ eModel = M51BX;
+ break;
+ default:
+ break;
+ }
+ }
+ return eModel;
+}
+
+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("Unable to create %s\n", tempFile);
+ free(strPDir);
+ return;
+ }
+
+ strncpy(model, ctrlp->mn, 40);
+ strncpy(serial, ctrlp->sn, 20);
+ strncpy(fwrev, ctrlp->fr, 8);
+
+ sprintf(strBuffer,
+ "********************\nDrive Info\n********************\n");
+
+ fprintf(fpOutFile, "%s", strBuffer);
+ sprintf(strBuffer,
+ "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n",
+ "Device Name", nFD,
+ "Model No", (char *)model,
+ "Serial No", (char *)serial, "FW-Rev", (char *)fwrev);
+
+ fprintf(fpOutFile, "%s", strBuffer);
+
+ sprintf(strBuffer,
+ "\n********************\nPCI Info\n********************\n");
+
+ fprintf(fpOutFile, "%s", strBuffer);
+
+ sprintf(strBuffer,
+ "%-22s : %04X\n%-22s : %04X\n",
+ "VendorId", vendor_id, "DeviceId", device_id);
+ fprintf(fpOutFile, "%s", strBuffer);
+ fclose(fpOutFile);
+ free(strPDir);
+}
+
+static void GetTimestampInfo(const char *strOSDirName)
+{
+ char outstr[200];
+ time_t t;
+ struct tm *tmp;
+ FILE *fpOutFile = NULL;
+ size_t num;
+ char tempFolder[256] = { 0 };
+ char *strPDir;
+ char *strDest;
+
+ t = time(NULL);
+ tmp = localtime(&t);
+ if (tmp == NULL)
+ return;
+
+ num = strftime(outstr, sizeof(outstr), "Timestamp (UTC): %a, %d %b %Y %T %z", tmp);
+ if (num) {
+ strPDir = strdup(strOSDirName);
+ strDest = dirname(strPDir);
+ sprintf(tempFolder, "%s/%s", strDest, "timestamp_info.txt");
+ fpOutFile = fopen(tempFolder, "wb");
+ if (fwrite(outstr, 1, num, fpOutFile) != num)
+ printf("Unable to write to %s file!", tempFolder);
+ if (fpOutFile)
+ fclose(fpOutFile);
+ free(strPDir);
+ }
+}
+
+static void GetCtrlIDDInfo(const char *strCtrlDirName, struct nvme_id_ctrl *ctrlp)
+{
+ char tempFolder[PATH_MAX] = { 0 };
+ FILE *fpOutFile;
+ sprintf(tempFolder, "%s/%s", strCtrlDirName,
+ "nvme_controller_identify_data.bin");
+ fpOutFile = fopen(tempFolder, "wb");
+ if (fwrite(ctrlp, 1, sizeof(*ctrlp), fpOutFile) != sizeof(*ctrlp))
+ printf("Unable to write controller data to %s file!", tempFolder);
+ if (fpOutFile)
+ fclose(fpOutFile);
+}
+
+static void GetSmartlogData(int fd, const char *strCtrlDirName)
+{
+ char tempFolder[PATH_MAX] = { 0 };
+ FILE *fpOutFile = NULL;
+ struct nvme_smart_log smart_log;
+ if (nvme_smart_log(fd, -1, &smart_log) == 0) {
+ sprintf(tempFolder, "%s/%s", strCtrlDirName, "smart_data.bin");
+ fpOutFile = fopen(tempFolder, "wb");
+ if (fwrite(&smart_log, 1, sizeof(smart_log), fpOutFile) != sizeof(smart_log))
+ printf("Unable to write smart log data to %s file!", tempFolder);
+ if (fpOutFile)
+ fclose(fpOutFile);
+ }
+}
+
+static void GetErrorlogData(int fd, int entries, const char *strCtrlDirName)
+{
+ char tempFolder[PATH_MAX] = { 0 };
+ FILE *fpOutFile = NULL;
+ int logSize = entries * sizeof(struct nvme_error_log_page);
+ struct nvme_error_log_page *error_log = (struct nvme_error_log_page *)calloc(1, logSize);
+
+ if (error_log == NULL)
+ return;
+
+ if (nvme_error_log(fd, entries, error_log) == 0) {
+ sprintf(tempFolder, "%s/%s", strCtrlDirName,
+ "error_information_log.bin");
+ fpOutFile = fopen(tempFolder, "wb");
+ if (fwrite(error_log, 1, logSize, fpOutFile) != logSize)
+ printf("Unable to write error log to %s file!", tempFolder);
+ if (fpOutFile)
+ fclose(fpOutFile);
+ }
+ free(error_log);
+}
+
+static void GetNSIDDInfo(int fd, const char *strCtrlDirName, int nsid)
+{
+ char tempFolder[256] = { 0 };
+ char strFileName[PATH_MAX] = { 0 };
+ FILE *fpOutFile = NULL;
+ struct nvme_id_ns ns;
+ if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) {
+ sprintf(tempFolder, "identify_namespace_%d_data.bin.bin", nsid);
+ sprintf(strFileName, "%s/%s", strCtrlDirName, tempFolder);
+ fpOutFile = fopen(strFileName, "wb");
+ if (fwrite(&ns, 1, sizeof(ns), fpOutFile) != sizeof(ns))
+ printf("Unable to write controller data to %s file!", tempFolder);
+ if (fpOutFile)
+ fclose(fpOutFile);
+ }
+}
+
+static void GetOSConfig(const char *strOSDirName)
+{
+ FILE *fpOSConfig = NULL;
+ char strBuffer[1024], strTemp[1024];
+ char strFileName[PATH_MAX];
+ int i;
+
+ struct {
+ char *strcmdHeader;
+ char *strCommand;
+ } cmdArray[] = {
+ { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" },
+ { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" },
+ { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" },
+ { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" },
+ { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" },
+ { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" },
+ { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" },
+ { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" },
+ { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" }
+ };
+
+ sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt");
+
+ for (i = 0; i < 7; i++) {
+ fpOSConfig = fopen(strFileName, "a+");
+ fprintf(fpOSConfig,
+ "\n\n\n\n%s\n-----------------------------------------------\n",
+ cmdArray[i].strcmdHeader);
+ if (NULL != fpOSConfig) {
+ fclose(fpOSConfig);
+ fpOSConfig = NULL;
+ }
+ strcpy(strTemp, cmdArray[i].strCommand);
+ sprintf(strBuffer, strTemp, strFileName);
+ if (system(strBuffer))
+ fprintf(stderr, "Failed to send \"%s\"\n", strBuffer);
+ }
+}
+
+static int micron_internal_logs(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+
+ int err = 0;
+ int fd;
+ int ctrlIdx;
+ FILE *fpOutFile = NULL;
+ char strOSDirName[1024];
+ char strCtrlDirName[1024];
+ char strMainDirName[256];
+ char tempFolder[PATH_MAX] = { 0 };
+ unsigned int *puiIDDBuf;
+ unsigned int uiMask;
+ struct nvme_id_ctrl ctrl;
+ char sn[20] = { 0 };
+
+ struct {
+ unsigned char ucLogPage;
+ const char *strFileName;
+ int nLogSize;
+ int nMaxSize;
+ } aVendorLogs[32] = {
+ { 0xC1, "nvmelog_C1.bin", 0, 0 },
+ { 0xC2, "nvmelog_C2.bin", 0, 0 },
+ { 0xC4, "nvmelog_C4.bin", 0, 0 },
+ { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 },
+ { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 },
+ { 0xE6, "nvmelog_E6.bin", 0, 0 },
+ { 0xE7, "nvmelog_E7.bin", 0, 0 }
+ },
+ aM51XXLogs[] = {
+ { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */
+ { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 },
+ { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 },
+ { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 },
+ { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 },
+ { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 }
+ },
+ aM51AXLogs[] = {
+ { 0xD0, "nvmelog_D0.bin", 512, 0 },
+ { 0xCA, "nvmelog_CA.bin", 512, 0 },
+ { 0xFA, "nvmelog_FA.bin", 4096, 15232 },
+ { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 },
+ { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 },
+ { 0x04, "changed_namespace_log.bin", 4096, 0 },
+ { 0x05, "command_effects_log.bin", 4096, 0 },
+ { 0x06, "drive_self_test.bin", 4096, 0 }
+ },
+ aM51BXLogs[] = {
+ { 0xFA, "nvmelog_FA.bin", 4096, 16376 },
+ { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 },
+ { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 },
+ { 0xCA, "nvmelog_CA.bin", 512, 1024 }
+ };
+
+ eDriveModel eModel;
+
+ const char *desc = "This retrieves the micron debug log package";
+ const char *package = "Log output package name (required)";
+ unsigned char *dataBuffer = NULL;
+ int bSize = 0;
+ int maxSize = 0;
+
+ struct config {
+ char *package;
+ };
+
+ struct config cfg = {
+ .package = ""
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("package", 'p', "FILE", &cfg.package, package),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (strlen(cfg.package) == 0) {
+ printf ("You must specify an output name for the log package. ie --p=logfiles.zip\n");
+ goto out;
+ }
+
+ if (fd < 0)
+ goto out;
+
+ /* pull log details based on the model name */
+ sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx);
+ if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) {
+ printf ("Unsupported drive model for vs-internal-log collection\n");
+ close(fd);
+ goto out;
+ }
+
+ printf("Preparing log package. This will take a few seconds...\n");
+ err = nvme_identify_ctrl(fd, &ctrl);
+ if (err)
+ goto out;
+
+ // trim spaces out of serial number string */
+ int i, j = 0;
+ for (i = 0; i < sizeof(ctrl.sn); i++) {
+ if (isblank(ctrl.sn[i]))
+ continue;
+ sn[j++] = ctrl.sn[i];
+ }
+ sn[j] = '\0';
+ strcpy(ctrl.sn, sn);
+
+ SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName);
+
+ GetTimestampInfo(strOSDirName);
+ GetCtrlIDDInfo(strCtrlDirName, &ctrl);
+ GetOSConfig(strOSDirName);
+ GetDriveInfo(strOSDirName, ctrlIdx, &ctrl);
+
+ for (int i = 1; i <= ctrl.nn; i++)
+ GetNSIDDInfo(fd, strCtrlDirName, i);
+
+ GetSmartlogData(fd, strCtrlDirName);
+ GetErrorlogData(fd, ctrl.elpe, strCtrlDirName);
+
+ if (eModel != M5410) {
+ memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs));
+ if (eModel == M51AX)
+ memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs));
+ else
+ memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs));
+ }
+
+ for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && aVendorLogs[i].ucLogPage != 0; i++) {
+ err = -1;
+ switch (aVendorLogs[i].ucLogPage) {
+ case 0xC1:
+ case 0xC2:
+ case 0xC4:
+ err = GetLogPageSize(fd, aVendorLogs[i].ucLogPage, &bSize);
+ if (err == 0 && bSize > 0)
+ err = GetCommonLogPage(fd, aVendorLogs[i].ucLogPage, &dataBuffer, bSize);
+ break;
+
+ case 0xE6:
+ case 0xE7:
+ puiIDDBuf = (unsigned int *)&ctrl;
+ uiMask = puiIDDBuf[1015];
+ if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || (aVendorLogs[i].ucLogPage == 0xE7
+ && uiMask == 1)) {
+ bSize = 0;
+ } else {
+ bSize = (int)puiIDDBuf[1015];
+ if (bSize % (16 * 1024)) {
+ bSize += (16 * 1024) - (bSize % (16 * 1024));
+ }
+ }
+ if (bSize != 0) {
+ dataBuffer = (unsigned char *)malloc(bSize);
+ memset(dataBuffer, 0, bSize);
+ err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize);
+ }
+ break;
+
+ case 0xF7:
+ case 0xF9:
+ case 0xFC:
+ case 0xFD:
+ if (eModel == M51BX)
+ (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize);
+ default:
+ bSize = aVendorLogs[i].nLogSize;
+ dataBuffer = (unsigned char *)malloc(bSize);
+ memset(dataBuffer, 0, bSize);
+ err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize);
+ maxSize = aVendorLogs[i].nMaxSize - bSize;
+ while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) {
+ sprintf(tempFolder, "%s/%s", strCtrlDirName,
+ aVendorLogs[i].strFileName);
+ fpOutFile = fopen(tempFolder, "ab+");
+ if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) {
+ printf ("Unable to write log to file %s\n!", aVendorLogs[i].strFileName);
+ }
+ if (fpOutFile)
+ fclose(fpOutFile);
+ err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize);
+ if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef))
+ break;
+ maxSize -= bSize;
+ }
+ break;
+ }
+
+ if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) {
+ sprintf(tempFolder, "%s/%s", strCtrlDirName,
+ aVendorLogs[i].strFileName);
+ fpOutFile = fopen(tempFolder, "ab+");
+ if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) {
+ printf("Unable to write log to file %s\n!", aVendorLogs[i].strFileName);
+ }
+ if (fpOutFile)
+ fclose(fpOutFile);
+ }
+
+ if (dataBuffer != NULL) {
+ free(dataBuffer);
+ dataBuffer = NULL;
+ }
+ }
+
+ ZipAndRemoveDir(strMainDirName, cfg.package);
+ out:
+ return err;
+}
diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h
new file mode 100644
index 0000000..add36e4
--- /dev/null
+++ b/plugins/micron/micron-nvme.h
@@ -0,0 +1,21 @@
+#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"),
+ 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-nand-stats", "Retrieve NAND Stats", micron_nand_stats)
+ )
+);
+
+#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..a942f57
--- /dev/null
+++ b/plugins/netapp/netapp-nvme.c
@@ -0,0 +1,639 @@
+/*
+* 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 <sys/ioctl.h>
+
+#include "nvme.h"
+#include "nvme-ioctl.h"
+#include "json.h"
+
+#include "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 {
+ int nsid;
+ struct nvme_id_ctrl ctrl;
+ struct nvme_id_ns ns;
+ char dev[265];
+};
+
+struct ontapdevice_info {
+ int nsid;
+ struct nvme_id_ctrl ctrl;
+ struct nvme_id_ns ns;
+ char nsdesc[4096];
+ 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, long long *lba,
+ struct nvme_id_ns *ns)
+{
+ *lba = 1 << ns->lbaf[(ns->flbas & 0x0F)].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 netapp_uuid_to_str(char *str, void *data)
+{
+#ifdef LIBUUID
+ uuid_t uuid;
+ struct nvme_ns_id_desc *desc = data;
+
+ memcpy(uuid, data + sizeof(*desc), 16);
+ uuid_unparse_lower(uuid, str);
+#endif
+}
+
+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_array *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_array *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_array *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 */
+
+ 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++) {
+ long long int lba = 1 << devices[i].ns.lbaf[(devices[i].ns.flbas & 0x0F)].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);
+ }
+}
+
+static void netapp_ontapdevices_print(struct ontapdevice_info *devices,
+ int count, int format)
+{
+ struct json_object *root = NULL;
+ struct json_array *json_devices = NULL;
+ char vsname[ONTAP_LABEL_LEN] = " ";
+ char nspath[ONTAP_NS_PATHLEN] = " ";
+ 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);
+ netapp_uuid_to_str(uuid_str, devices[i].nsdesc);
+ 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);
+ }
+}
+
+static int nvme_get_ontap_c2_log(int fd, __u32 nsid, void *buf, __u32 buflen)
+{
+ struct nvme_admin_cmd get_log;
+ int err;
+
+ memset(buf, 0, buflen);
+ memset(&get_log, 0, sizeof(struct nvme_admin_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);
+ 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,
+ strerror(err));
+ return 0;
+ }
+
+ if (strncmp("NetApp E-Series", item->ctrl.mn, 15) != 0)
+ return 0; /* not the right model of controller */
+
+ item->nsid = nvme_get_nsid(fd);
+ err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
+ if (err) {
+ fprintf(stderr, "Unable to identify namespace for %s (%s)\n",
+ dev, strerror(err));
+ return 0;
+ }
+ strncpy(item->dev, dev, sizeof(item->dev));
+
+ return 1;
+}
+
+static int netapp_ontapdevices_get_info(int fd, struct ontapdevice_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, strerror(err));
+ return 0;
+ }
+
+ if (strncmp("NetApp ONTAP Controller", item->ctrl.mn, 23) != 0)
+ /* not the right controller model */
+ return 0;
+
+ item->nsid = nvme_get_nsid(fd);
+
+ err = nvme_identify_ns(fd, item->nsid, 0, &item->ns);
+ if (err) {
+ fprintf(stderr, "Unable to identify namespace for %s (%s)\n",
+ dev, strerror(err));
+ return 0;
+ }
+
+ err = nvme_identify_ns_descs(fd, item->nsid, item->nsdesc);
+ if (err) {
+ fprintf(stderr, "Unable to identify namespace descriptor for %s (%s)\n",
+ dev, strerror(err));
+ return 0;
+ }
+
+ 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, strerror(err));
+ return 0;
+ }
+
+ strncpy(item->dev, dev, sizeof(item->dev));
+
+ 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..d4eebd6
--- /dev/null
+++ b/plugins/netapp/netapp-nvme.h
@@ -0,0 +1,18 @@
+#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"),
+ COMMAND_LIST(
+ ENTRY("smdevices", "NetApp SMdevices", netapp_smdevices)
+ ENTRY("ontapdevices", "NetApp ONTAPdevices", netapp_ontapdevices)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c
new file mode 100644
index 0000000..846ca77
--- /dev/null
+++ b/plugins/scaleflux/sfx-nvme.c
@@ -0,0 +1,873 @@
+#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/ioctl.h>
+#include <sys/sysinfo.h>
+
+#include "linux/nvme_ioctl.h"
+
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "nvme-status.h"
+#include "json.h"
+#include "plugin.h"
+
+#include "argconfig.h"
+#include "suffix.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 IDEMA_CAP(exp_GB) (((__u64)exp_GB - 50ULL) * 1953504ULL + 97696368ULL)
+
+
+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,
+};
+
+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 */
+};
+
+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 {
+ uint8_t key;
+ uint8_t _kp[2];
+ uint8_t norm;
+ uint8_t _np;
+ union {
+ uint8_t raw[6];
+ struct wear_level {
+ uint16_t min;
+ uint16_t max;
+ uint16_t avg;
+ } wear_level ;
+ struct thermal_throttle {
+ uint8_t pct;
+ uint32_t count;
+ } thermal_throttle;
+ };
+ uint8_t _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
+};
+
+int nvme_change_cap(int fd, __u32 nsid, __u64 capacity)
+{
+ struct nvme_admin_cmd cmd = {
+ .opcode = nvme_admin_change_cap,
+ .nsid = nsid,
+ .cdw10 = (capacity & 0xffffffff),
+ .cdw11 = (capacity >> 32),
+ };
+
+
+ return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd);
+}
+
+int nvme_sfx_set_features(int fd, __u32 nsid, __u32 fid, __u32 value)
+{
+ struct nvme_admin_cmd cmd = {
+ .opcode = nvme_admin_sfx_set_features,
+ .nsid = nsid,
+ .cdw10 = fid,
+ .cdw11 = value,
+ };
+
+ return nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd);
+}
+
+int nvme_sfx_get_features(int fd, __u32 nsid, __u32 fid, __u32 *result)
+{
+ int err = 0;
+ struct nvme_admin_cmd cmd = {
+ .opcode = nvme_admin_sfx_get_features,
+ .nsid = nsid,
+ .cdw10 = fid,
+ };
+
+ err = nvme_submit_passthru(fd, NVME_IOCTL_ADMIN_CMD,&cmd);
+ 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);
+
+ 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));
+}
+
+static int get_additional_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct nvme_additional_smart_log smart_log;
+ int err, fd;
+ 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 config {
+ __u32 namespace_id;
+ int raw_binary;
+ int json;
+ };
+
+ 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()
+ };
+
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ err = nvme_get_log(fd, cfg.namespace_id, 0xca, false, sizeof(smart_log),
+ (void *)&smart_log);
+ if (!err) {
+ if (cfg.json)
+ show_sfx_smart_log_jsn(&smart_log, cfg.namespace_id, devicename);
+ else if (!cfg.raw_binary)
+ show_sfx_smart_log(&smart_log, cfg.namespace_id, devicename);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ return err;
+}
+
+
+struct sfx_lat_stats {
+ __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+ */
+};
+
+static void show_lat_stats(struct sfx_lat_stats *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 int get_lat_stats_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct sfx_lat_stats stats;
+ int err, fd;
+
+ 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 config {
+ int raw_binary;
+ int write;
+ };
+
+ struct config cfg = {
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("write", 'w', &cfg.write, write),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ err = nvme_get_log(fd, 0xffffffff, cfg.write ? 0xc3 : 0xc1, false, sizeof(stats), (void *)&stats);
+ if (!err) {
+ if (!cfg.raw_binary)
+ show_lat_stats(&stats, cfg.write);
+ else
+ d_raw((unsigned char *)&stats, sizeof(stats));
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ return err;
+}
+
+int sfx_nvme_get_log(int fd, __u32 nsid, __u8 log_id, __u32 data_len, void *data)
+{
+ struct nvme_admin_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);
+}
+
+/**
+ * @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%llx", *(bb_elem++));
+ i++;
+ }
+ printf(" ]\n");
+
+ printf("REMAP_GBB_TABLE [");
+ i = 0;
+ while (bb_elem < elem_end && i < remap_gbb_count) {
+ printf(" 0x%llx",*(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)
+{
+ int fd;
+ unsigned char *data_buf;
+ const __u64 buf_size = 256*4096*sizeof(unsigned char);
+ int err = 0;
+
+ char *desc = "Get bad block table of sfx block device.";
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (fd < 0) {
+ return fd;
+ }
+
+ data_buf = malloc(buf_size);
+ if (!data_buf) {
+ fprintf(stderr, "malloc fail, errno %d\r\n", errno);
+ return -1;
+ }
+
+ err = get_bb_table(fd, 0xffffffff, data_buf, buf_size);
+ if (err < 0) {
+ perror("get-bad-block");
+ } else if (err != 0) {
+ fprintf(stderr, "NVMe IO command error:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ } else {
+ bd_table_show(data_buf, buf_size);
+ printf("ScaleFlux get bad block table: success\n");
+ }
+
+ free(data_buf);
+ return 0;
+}
+
+static void show_cap_info(struct sfx_freespace_ctx *ctx)
+{
+ printf("user sectors: %#llx\n", ctx->user_space);
+ printf("totl physical sectors: %#llx\n", ctx->phy_space);
+ printf("free physical sectors: %#llx\n", ctx->free_space);
+ printf("used physical sectors: %#llx\n", ctx->phy_space - ctx->free_space);
+}
+
+static int query_cap_info(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ struct sfx_freespace_ctx ctx = { 0 };
+ int err = 0, fd;
+ char *desc = "query current capacity info of vanda";
+ const char *raw = "dump output in binary format";
+ const char *json= "Dump output in json format";
+ struct config {
+ int raw_binary;
+ int json;
+ };
+ struct config cfg;
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_FLAG("json", 'j', &cfg.json, json),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ return fd;
+ }
+
+ if (ioctl(fd, SFX_GET_FREESPACE, &ctx)) {
+ fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno);
+ return -1;
+ }
+
+ show_cap_info(&ctx);
+ return err;
+}
+
+static int change_cap_mem_check(int fd, __u64 trg_in_4k)
+{
+ struct sfx_freespace_ctx freespace_ctx = { 0 };
+ struct sysinfo s_info;
+ __u64 mem_need = 0;
+ __u64 cur_in_4k = 0;
+ __u32 cnt_ms = 0;
+
+ while (ioctl(fd, SFX_GET_FREESPACE, &freespace_ctx)) {
+ if (cnt_ms++ > 600) {//1min
+ fprintf(stderr, "vu ioctl fail, errno %d\r\n", errno);
+ return -1;
+ }
+ usleep(100000);
+ }
+
+ cur_in_4k = freespace_ctx.user_space >> (SFX_PAGE_SHIFT - SECTOR_SHIFT);
+ if (cur_in_4k > trg_in_4k) {
+ return 0;
+ }
+
+ 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: mem needed is %llu, free mem is %lu\n"
+ "Insufficient memory, please drop cache or add free memory and retry\n",
+ mem_need, s_info.freeram);
+ return -1;
+ }
+ return 0;
+}
+
+static int change_cap(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int err = -1, fd;
+ char *desc = "query current capacity info of vanda";
+ const char *raw = "dump output in binary format";
+ const char *json= "Dump output in json format";
+ 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";
+ __u64 cap_in_4k = 0;
+ __u64 cap_in_sec = 0;
+ struct config {
+ __u64 cap_in_byte;
+ __u32 capacity_in_gb;
+ int raw_binary;
+ int json;
+ int 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_UINT("cap-byte", 'z', &cfg.cap_in_byte, cap_byte),
+ OPT_FLAG("force", 'f', &cfg.force, force),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_FLAG("json", 'j', &cfg.json, json),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ return fd;
+ }
+
+ if (!cfg.force) {
+ fprintf(stderr, "WARNING: Changing capacity may irrevocably delete user data.\n"
+ "You have 10 seconds to press Ctrl-C to cancel this operation.\n\n"
+ "Use the force [--force|-f] option to suppress this warning.\n");
+ sleep(10);
+ fprintf(stderr, "Sending operation ... \n");
+ }
+
+ cap_in_sec = IDEMA_CAP(cfg.capacity_in_gb);
+ cap_in_4k = cap_in_sec >> 3;
+ if (cfg.cap_in_byte)
+ cap_in_4k = cfg.cap_in_byte >> 12;
+ printf("%dG %lluB %llu 4K\n",
+ cfg.capacity_in_gb, cfg.cap_in_byte, cap_in_4k);
+ if (change_cap_mem_check(fd, cap_in_4k))
+ return err;
+
+ err = nvme_change_cap(fd, 0xffffffff, cap_in_4k);
+ if (err < 0)
+ perror("sfx-change-cap");
+ else if (err != 0)
+ fprintf(stderr, "NVMe IO command error:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ else {
+ printf("ScaleFlux change-capacity: success\n");
+ if(ioctl(fd, BLKRRPART) < 0) {
+ fprintf(stderr, "failed to re-read partition table\n");
+ err = EFAULT;
+ }
+ }
+ return err;
+}
+
+char *sfx_feature_to_string(int feature)
+{
+ switch (feature) {
+ case SFX_FEAT_ATOMIC: return "ATOMIC";
+
+ default: return "Unknown";
+ }
+}
+
+static int sfx_set_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int err = 0, fd;
+ char *desc = "ScaleFlux internal set features\n"
+ "feature id 1: ATOMIC";
+ const char *value = "new value of feature (required)";
+ const char *feature_id = "hex feature name (required)";
+ const char *namespace_id = "desired namespace";
+ struct nvme_id_ns ns;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 feature_id;
+ __u32 value;
+ };
+ struct config cfg = {
+ .namespace_id = 1,
+ .feature_id = 0,
+ .value = 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_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ return fd;
+ }
+
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ return EINVAL;
+ }
+
+ if (cfg.feature_id == SFX_FEAT_ATOMIC) {
+ if (cfg.namespace_id != 0xffffffff) {
+ err = nvme_identify_ns(fd, cfg.namespace_id, 0, &ns);
+ if (err) {
+ if (err < 0)
+ perror("identify-namespace");
+ else
+ fprintf(stderr,
+ "NVMe Admin command error:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ 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");
+ return EFAULT;
+ }
+ }
+ }
+
+ err = nvme_sfx_set_features(fd, cfg.namespace_id, cfg.feature_id, cfg.value);
+ if (err < 0) {
+ perror("ScaleFlux-set-feature");
+ return errno;
+ } else if (!err) {
+ printf("ScaleFlux set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
+ sfx_feature_to_string(cfg.feature_id), cfg.value);
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+
+ return err;
+}
+
+static int sfx_get_feature(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int err = 0, fd;
+ 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";
+ __u32 result = 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (fd < 0) {
+ return fd;
+ }
+
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ return EINVAL;
+ }
+
+ err = nvme_sfx_get_features(fd, cfg.namespace_id, cfg.feature_id, &result);
+ if (err < 0) {
+ perror("ScaleFlux-get-feature");
+ 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+
+ return err;
+
+}
diff --git a/plugins/scaleflux/sfx-nvme.h b/plugins/scaleflux/sfx-nvme.h
new file mode 100644
index 0000000..daf9c33
--- /dev/null
+++ b/plugins/scaleflux/sfx-nvme.h
@@ -0,0 +1,25 @@
+#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"),
+ 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..3dc2df7
--- /dev/null
+++ b/plugins/seagate/seagate-diag.h
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+
+#ifndef SEAGATE_NVME_H
+#define SEAGATE_NVME_H
+
+#define SEAGATE_PLUGIN_VERSION_MAJOR 1
+#define SEAGATE_PLUGIN_VERSION_MINOR 1
+
+#define PERSIST_FILE_SIZE (2764800)
+#define ONE_MB (1048576) /* (1024 * 1024) */
+#define PERSIST_CHUNK (65536) /* (1024 * 64) */
+#define FOUR_KB (4096)
+
+
+/***************************
+*Supported Log-Pages from FW
+***************************/
+
+typedef struct 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 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
+***************************/
+#pragma pack(1)
+#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 _SmartVendorSpecific
+{
+ __u8 AttributeNumber;
+ __u16 SmartStatus;
+ __u8 NominalValue;
+ __u8 LifetimeWorstValue;
+ __u32 Raw0_3;
+ __u8 RawHigh[3];
+} SmartVendorSpecific;
+
+typedef struct _EXTENDED_SMART_INFO_T
+{
+ __u16 Version;
+ SmartVendorSpecific vendorData[NUMBER_EXTENDED_SMART_ATTRIBUTES];
+ __u8 vendor_specific_reserved[6];
+} EXTENDED_SMART_INFO_T;
+
+typedef struct vendor_smart_attribute_data
+{
+ __u8 AttributeNumber; /* 00 */
+ __u8 Rsvd[3]; /* 01 -03 */
+ __u32 LSDword; /* 04-07 */
+ __u32 MSDword; /* 08 - 11 */
+} vendor_smart_attribute_data;
+
+struct 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 _U128
+{
+ __u64 LS__u64;
+ __u64 MS__u64;
+} U128;
+
+typedef struct _vendor_log_page_CF_Attr
+{
+ __u16 SuperCapCurrentTemperature; /* 00-01 */
+ __u16 SuperCapMaximumTemperature; /* 02-03 */
+ __u8 SuperCapStatus; /* 04 */
+ __u8 Reserved5to7[3]; /* 05-07 */
+ U128 DataUnitsReadToDramNamespace; /* 08-23 */
+ U128 DataUnitsWrittenToDramNamespace; /* 24-39 */
+ __u64 DramCorrectableErrorCount; /* 40-47 */
+ __u64 DramUncorrectableErrorCount; /* 48-55 */
+}vendor_log_page_CF_Attr;
+
+typedef struct _vendor_log_page_CF
+{
+ vendor_log_page_CF_Attr AttrCF;
+ __u8 Vendor_Specific_Reserved[ 456 ]; /* 56-511 */
+}vendor_log_page_CF;
+
+#pragma pack()
+/* EOF Extended-SMART Information*/
+
+
+/**************************
+* PCIE ERROR INFORMATION
+**************************/
+typedef struct 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 */
+
+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 362-372 */
+ VS_ATTR_LIFETIME_WRITES1_TO_FLASH, /* 31 374-385 */
+ VS_ATTR_LIFETIME_WRITES0_FROM_HOST, /* 32 386-397 */
+ VS_ATTR_LIFETIME_WRITES1_FROM_HOST, /* 33 398-409 */
+ VS_ATTR_LIFETIME_READ0_FROM_HOST, /* 34 410-421 */
+ VS_ATTR_LIFETIME_READ1_FROM_HOST, /* 35 422-433 */
+ VS_ATTR_PCIE_PHY_CRC_ERROR, /* 36 434-445 */
+ VS_ATTR_BAD_BLOCK_COUNT_SYSTEM, /* 37 446-457 */
+ VS_ATTR_BAD_BLOCK_COUNT_USER, /* 38 458-469 */
+ VS_ATTR_THERMAL_THROTTLING_STATUS, /* 39 470-481 */
+ VS_ATTR_POWER_CONSUMPTION, /* 40 482-493 */
+ VS_ATTR_MAX_SOC_LIFE_TEMPERATURE, /* 41 494-505 */
+
+ 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..7ba14f8
--- /dev/null
+++ b/plugins/seagate/seagate-nvme.c
@@ -0,0 +1,1397 @@
+/*
+ * 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.
+ */
+
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include "linux/nvme_ioctl.h"
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+#include "argconfig.h"
+#include "suffix.h"
+#include "json.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 void json_log_pages_supp(log_page_map *logPageMap)
+{
+ struct json_object *root;
+ struct json_array *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);
+ printf("\n");
+}
+
+static int log_pages_supp(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ int err = 0;
+ int fd = 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";
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ err = nvme_get_log(fd, 1, 0xc5, false, 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ 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"; /*VS_ATTR_ID_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";/*VS_ATTR_ID_MAX_LIFE_TEMPERATURE for extended*/
+ 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 read error count";/*VS_ATTR_ID_UNCORRECTABLE_RAISE_ERRORS*/
+ 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"", (uint64_t)smart_attribute_vs(verNo, attr));
+ printf("\n");
+ }
+
+ 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", buf);
+ printf("\n");
+
+ 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", buf);
+ printf("\n");
+
+ 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", buf);
+ printf("\n");
+
+ 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", buf);
+ printf("\n");
+
+ 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", buf);
+ printf("\n");
+
+ }
+}
+
+static void json_print_smart_log(struct json_object *root,
+ EXTENDED_SMART_INFO_T *ExtdSMARTInfo )
+{
+ /*struct json_object *root; */
+ struct json_array *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};
+
+ /*root = json_create_object();*/
+ 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);
+
+ /*
+ json_print_object(root, NULL);
+ printf("\n");
+ */
+}
+
+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;
+ /*currentTemp = currentTemp ? currentTemp - 273 : 0;*/
+ printf(" 0x%016"PRIx64"", le64_to_cpu(currentTemp));
+ printf("\n");
+
+ maxTemp = pLogPageCF->AttrCF.SuperCapMaximumTemperature;
+ /*maxTemp = maxTemp ? maxTemp - 273 : 0;*/
+ printf("%-40s", "Super-cap maximum temperature");
+ printf(" 0x%016"PRIx64"", le64_to_cpu(maxTemp));
+ printf("\n");
+
+ printf("%-40s", "Super-cap status");
+ printf(" 0x%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.SuperCapStatus));
+ printf("\n");
+
+ printf("%-40s", "Data units read to DRAM namespace");
+ printf(" 0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.MS__u64),
+ le64_to_cpu(pLogPageCF->AttrCF.DataUnitsReadToDramNamespace.LS__u64));
+ printf("\n");
+
+ printf("%-40s", "Data units written to DRAM namespace");
+ printf(" 0x%016"PRIx64"%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.MS__u64),
+ le64_to_cpu(pLogPageCF->AttrCF.DataUnitsWrittenToDramNamespace.LS__u64));
+ printf("\n");
+
+ printf("%-40s", "DRAM correctable error count");
+ printf(" 0x%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DramCorrectableErrorCount));
+ printf("\n");
+
+ printf("%-40s", "DRAM uncorrectable error count");
+ printf(" 0x%016"PRIx64"", le64_to_cpu(pLogPageCF->AttrCF.DramUncorrectableErrorCount));
+ printf("\n");
+
+}
+
+static void json_print_smart_log_CF(struct json_object *root,
+ vendor_log_page_CF *pLogPageCF)
+{
+ /*struct json_object *root;*/
+ struct json_array *logPages;
+ unsigned int currentTemp, maxTemp;
+ char buf[40];
+
+ /*root = json_create_object(); */
+
+ 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;
+ /*currentTemp = currentTemp ? currentTemp - 273 : 0;*/
+ 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;
+ /*maxTemp = maxTemp ? maxTemp - 273 : 0;*/
+ 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);
+
+ /*
+ json_print_object(root, NULL);
+ printf("\n");
+ */
+}
+
+static int vs_smart_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ EXTENDED_SMART_INFO_T ExtdSMARTInfo;
+ vendor_log_page_CF logPageCF;
+ int fd;
+ struct json_object *root;
+ struct json_array *lbafs;
+ struct json_object *lbafs_ExtSmart, *lbafs_DramSmart;
+ root = json_create_object();
+ lbafs = json_create_array();
+
+ const char *desc = "Retrieve Seagate Extended SMART information for the given device ";
+ const char *output_format = "output in binary format";
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (strcmp(cfg.output_format,"json"))
+ printf("Seagate Extended SMART Information :\n");
+
+ err = nvme_get_log(fd, 1, 0xC4, false, 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(fd, 1, 0xCF, false, sizeof(logPageCF), &logPageCF);
+ if (!err) {
+ if(strcmp(cfg.output_format,"json")) {
+ /*printf("Seagate DRAM Supercap SMART Attributes :\n");*/
+
+ 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+
+ 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);
+ printf("\n");
+
+}
+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 fd;
+ 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 config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, output_format),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ 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_smart_log(fd, 0xffffffff, &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(fd, 1, 0xC4, false, 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+
+ cf_err = nvme_get_log(fd, 1, 0xCF, false, sizeof(ExtdSMARTInfo), &logPageCF);
+
+ 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);
+
+ 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);
+ printf("\n");
+}
+
+static int vs_pcie_error_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ pcie_error_log_page pcieErrorLog;
+ int fd;
+
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if(strcmp(cfg.output_format,"json"))
+ printf("Seagate PCIe error counters Information :\n");
+
+ err = nvme_get_log(fd, 1, 0xCB, false, 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(err), err);
+
+ return err;
+}
+/* EOF PCIE error-log information */
+
+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";
+ int err, fd;
+ __u32 result;
+ void *buf = NULL;
+
+ struct config {
+ int save;
+ };
+
+ struct config cfg = {
+ .save = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FLAG("save", 's', &cfg.save, save),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ err = nvme_set_feature(fd, 0, 0xE1, 0xCB, 0, cfg.save, 0, buf, &result);
+
+ if (err < 0) {
+ perror("set-feature");
+ return errno;
+ }
+
+ 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";
+ int err, fd, dump_fd;
+ struct nvme_temetry_log_hdr tele_log;
+ __le64 offset = 0;
+ int blkCnt, maxBlk = 0, blksToGet;
+ unsigned char *log;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 log_id;
+ int 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ dump_fd = STDOUT_FILENO;
+ cfg.log_id = (cfg.log_id << 8) | 0x07;
+ err = nvme_get_log13(fd, cfg.namespace_id, cfg.log_id,
+ NVME_NO_LOG_LSP, offset, 0, false,
+ 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",
+ devicename, 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ else
+ perror("log page");
+
+ blkCnt = 0;
+
+ while(blkCnt < maxBlk) {
+ blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt);
+
+ if(blksToGet == 0)
+ return err;
+
+ log = malloc(blksToGet * 512);
+
+ if (!log) {
+ fprintf(stderr, "could not alloc buffer for log\n");
+ return EINVAL;
+ }
+
+ memset(log, 0, blksToGet * 512);
+
+ err = nvme_get_log13(fd, cfg.namespace_id, cfg.log_id,
+ NVME_NO_LOG_LSP, offset, 0, false,
+ blksToGet * 512, (void *)log);
+ if (!err) {
+ offset += blksToGet * 512;
+
+ if (!cfg.raw_binary) {
+ printf("\nBlock # :%d to %d\n", blkCnt + 1, blkCnt + blksToGet);
+
+ d((unsigned char *)log, blksToGet * 512, 16, 1);
+ } else
+ seaget_d_raw((unsigned char *)log, blksToGet * 512, dump_fd);
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ else
+ perror("log page");
+
+ blkCnt += blksToGet;
+
+ free(log);
+ }
+
+ 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";
+ int err, fd, 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;
+ int 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ dump_fd = STDOUT_FILENO;
+
+ log_id = 0x08;
+ err = nvme_get_log13(fd, cfg.namespace_id, log_id,
+ NVME_NO_LOG_LSP, offset, 0, false,
+ 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",
+ devicename, 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)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ else
+ perror("log page");
+
+ blkCnt = 0;
+
+ while(blkCnt < maxBlk) {
+ blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt);
+
+ if(blksToGet == 0)
+ return err;
+
+ log = malloc(blksToGet * 512);
+
+ if (!log) {
+ fprintf(stderr, "could not alloc buffer for log\n");
+ return EINVAL;
+ }
+
+ memset(log, 0, blksToGet * 512);
+
+ err = nvme_get_log13(fd, cfg.namespace_id, log_id,
+ NVME_NO_LOG_LSP, offset, 0, false,
+ blksToGet * 512, (void *)log);
+ if (!err) {
+ offset += blksToGet * 512;
+
+ if (!cfg.raw_binary) {
+ printf("\nBlock # :%d to %d\n", blkCnt + 1, blkCnt + blksToGet);
+
+ d((unsigned char *)log, blksToGet * 512, 16, 1);
+ } else
+ seaget_d_raw((unsigned char *)log, blksToGet * 512, dump_fd);
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ else
+ perror("log page");
+
+ blkCnt += blksToGet;
+
+ free(log);
+ }
+ return err;
+
+}
+
+void seaget_d_raw(unsigned char *buf, int len, int fd)
+{
+ /*********************
+ int i;
+ fflush(stdout);
+ for (i = 0; i < len; i++)
+ putchar(*(buf+i));
+ *********************/
+
+ 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";
+ int err, fd, 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ dump_fd = STDOUT_FILENO;
+ if(strlen(cfg.file)) {
+ dump_fd = open(cfg.file, flags, mode);
+ if (dump_fd < 0) {
+ perror(cfg.file);
+ return EINVAL;
+ }
+ }
+
+ log_id = 0x08;
+ err = nvme_get_log13(fd, cfg.namespace_id, log_id,
+ NVME_NO_LOG_LSP, offset, 0, false,
+ sizeof(tele_log), (void *)(&tele_log));
+ if (!err) {
+ maxBlk = tele_log.tele_data_area3;
+ offset += 512;
+
+ /*
+ 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);
+ */
+ seaget_d_raw((unsigned char *)(&tele_log), sizeof(tele_log), dump_fd);
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ else
+ perror("log page");
+
+ blkCnt = 0;
+
+ while(blkCnt < maxBlk) {
+ blksToGet = ((maxBlk - blkCnt) >= TELEMETRY_BLOCKS_TO_READ) ? TELEMETRY_BLOCKS_TO_READ : (maxBlk - blkCnt);
+
+ if(blksToGet == 0) {
+ return err;
+ }
+
+ log = malloc(blksToGet * 512);
+
+ if (!log) {
+ fprintf(stderr, "could not alloc buffer for log\n");
+ return EINVAL;
+ }
+
+ memset(log, 0, blksToGet * 512);
+
+ err = nvme_get_log13(fd, cfg.namespace_id, log_id,
+ NVME_NO_LOG_LSP, offset, 0, false,
+ blksToGet * 512, (void *)log);
+ if (!err) {
+ offset += blksToGet * 512;
+
+ seaget_d_raw((unsigned char *)log, blksToGet * 512, dump_fd);
+
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ else
+ perror("log page");
+
+ blkCnt += blksToGet;
+
+ free(log);
+ }
+
+ if(strlen(cfg.file))
+ close(dump_fd);
+
+ 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 */
diff --git a/plugins/seagate/seagate-nvme.h b/plugins/seagate/seagate-nvme.h
new file mode 100644
index 0000000..f570697
--- /dev/null
+++ b/plugins/seagate/seagate-nvme.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#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"),
+ 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("plugin-version", "Shows Seagate plugin's version information ", seagate_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..3aa6f8a
--- /dev/null
+++ b/plugins/shannon/shannon-nvme.c
@@ -0,0 +1,374 @@
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "linux/nvme_ioctl.h"
+
+#include "common.h"
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "json.h"
+#include "plugin.h"
+
+#include "argconfig.h"
+#include "suffix.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;
+ int err, fd;
+ 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 config {
+ __u32 namespace_id;
+ int raw_binary;
+ };
+
+ struct config cfg = {
+ .namespace_id = NVME_NSID_ALL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace),
+ OPT_FLAG("raw-binary", 'b', &cfg.raw_binary, raw),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ err = nvme_get_log(fd, cfg.namespace_id, 0xca, false,
+ sizeof(smart_log), &smart_log);
+ if (!err) {
+ if (!cfg.raw_binary)
+ show_shannon_smart_log(&smart_log, cfg.namespace_id, devicename);
+ else
+ d_raw((unsigned char *)&smart_log, sizeof(smart_log));
+ }
+ else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ 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 "\
+ "behaviour of the feature. Each Feature has three possible "\
+ "settings: default, saveable, and current. If a Feature is "\
+ "saveable, it may be modified by set-feature. Default values "\
+ "are vendor-specific and not changeable. Use set-feature to "\
+ "change saveable Features.\n\n"\
+ "Available additional feature id:\n"\
+ "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";
+ int err, fd;
+ __u32 result;
+ void *buf = NULL;
+
+ struct config {
+ __u32 namespace_id;
+ __u32 feature_id;
+ __u8 sel;
+ __u32 cdw11;
+ __u32 data_len;
+ int raw_binary;
+ int human_readable;
+ };
+
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (cfg.sel > 7) {
+ fprintf(stderr, "invalid 'select' param:%d\n", cfg.sel);
+ close(fd);
+ return EINVAL;
+ }
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ close(fd);
+ return EINVAL;
+ }
+ if (cfg.data_len) {
+ if (posix_memalign(&buf, getpagesize(), cfg.data_len))
+ {
+ close(fd);
+ exit(ENOMEM);
+ }
+ memset(buf, 0, cfg.data_len);
+ }
+
+ err = nvme_get_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.sel, cfg.cdw11,
+ cfg.data_len, buf, &result);
+ if (!err) {
+ printf("get-feature:0x%02x (%s), %s value: %#08x\n", cfg.feature_id,
+ 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);
+ }
+ }
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), 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 err, fd;
+ __u32 result;
+ void *buf = NULL;
+ int ffd = STDIN_FILENO;
+
+ struct config {
+ char *file;
+ __u32 namespace_id;
+ __u32 feature_id;
+ __u32 value;
+ __u32 data_len;
+ int 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!cfg.feature_id) {
+ fprintf(stderr, "feature-id required param\n");
+ close(fd);
+ return EINVAL;
+ }
+
+ if (cfg.data_len) {
+ if (posix_memalign(&buf, getpagesize(), cfg.data_len)){
+ fprintf(stderr, "can not allocate feature payload\n");
+ close(fd);
+ 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;
+ }
+ }
+
+ err = nvme_set_feature(fd, cfg.namespace_id, cfg.feature_id, cfg.value,
+ 0, cfg.save, cfg.data_len, buf, &result);
+ if (err < 0) {
+ perror("set-feature");
+ goto free;
+ }
+ if (!err) {
+ printf("set-feature:%02x (%s), value:%#08x\n", cfg.feature_id,
+ nvme_feature_to_string(cfg.feature_id), cfg.value);
+ if (buf)
+ d(buf, cfg.data_len, 16, 1);
+ } else if (err > 0)
+ fprintf(stderr, "NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+
+free:
+ if (buf)
+ free(buf);
+ return err;
+}
+
+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..d40732e
--- /dev/null
+++ b/plugins/shannon/shannon-nvme.h
@@ -0,0 +1,20 @@
+#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"),
+ COMMAND_LIST(
+ ENTRY("smart-log-add", "Retrieve Shannon SMART Log, show it", get_additional_smart_log)
+ ENTRY("get-feature-add", "Get Shannon feature and show the resulting value", get_additional_feature)
+ ENTRY("set-feature-add", "Set a Shannon feature and show the resulting value", set_additional_feature)
+ ENTRY("id-ctrl", "Shannon NVMe Identify Controller", shannon_id_ctrl)
+ )
+);
+
+#endif
+
+#include "define_cmd.h"
diff --git a/plugins/toshiba/toshiba-nvme.c b/plugins/toshiba/toshiba-nvme.c
new file mode 100644
index 0000000..c067ce8
--- /dev/null
+++ b/plugins/toshiba/toshiba-nvme.c
@@ -0,0 +1,563 @@
+#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 "linux/nvme_ioctl.h"
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+#include "argconfig.h"
+#include "suffix.h"
+
+#define CREATE_CMD
+#include "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_passthru(fd, NVME_IOCTL_ADMIN_CMD, 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);
+ 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(int fd, __u32 nsid, const char *devname,
+ struct nvme_xdn_smart_log_c0 *smart)
+{
+ printf("Vendor Log Page Directory 0xC0 for NVME device:%s namespace-id:%x\n",
+ devname, 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(int fd, __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(fd, MASK_0 | MASK_1);
+ if (err) {
+ goto end;
+ }
+ err = nvme_get_log(fd, namespace_id, log_page, false,
+ 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(fd, namespace_id, devicename,
+ (struct nvme_xdn_smart_log_c0 *)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)
+{
+ int err, fd;
+ 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 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ 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(fd, 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)
+ fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(err), err);
+ return err;
+}
+
+static int internal_log(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int err, fd;
+ 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 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ 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(fd, cfg.output_file, !cfg.prev_log);
+ if (err < 0)
+ fprintf(stderr, "%s: couldn't get fw log \n", __func__);
+ if (err > 0)
+ fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__,
+ nvme_status_to_string(err), err);
+ return err;
+}
+
+static int clear_correctable_errors(int argc, char **argv, struct command *cmd,
+ struct plugin *plugin)
+{
+ int err, fd;
+ 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;
+ __u32 result;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ fprintf(stderr,"%s: failed to parse arguments\n", __func__);
+ return EINVAL;
+ }
+
+ /* Check device supported */
+ err = nvme_get_sct_status(fd, MASK_0 | MASK_1);
+ if (err)
+ goto end;
+
+ err = nvme_set_feature(fd, namespace_id, feature_id, value, cdw12, save,
+ 0, NULL, &result);
+ if (err)
+ fprintf(stderr, "%s: couldn't clear PCIe correctable errors \n",
+ __func__);
+end:
+ if (err > 0)
+ fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__,
+ nvme_status_to_string(err), err);
+ return err;
+}
diff --git a/plugins/toshiba/toshiba-nvme.h b/plugins/toshiba/toshiba-nvme.h
new file mode 100644
index 0000000..c405e78
--- /dev/null
+++ b/plugins/toshiba/toshiba-nvme.h
@@ -0,0 +1,20 @@
+#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"),
+ 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..dbb56be
--- /dev/null
+++ b/plugins/transcend/transcend-nvme.c
@@ -0,0 +1,92 @@
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "linux/nvme_ioctl.h"
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+#include "argconfig.h"
+#include "suffix.h"
+
+#define CREATE_CMD
+#include "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 result=0, fd;
+ int percent_used = 0, healthvalue=0;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (fd < 0) {
+ printf("\nDevice not found \n");;
+ return -1;
+ }
+ result = nvme_smart_log(fd, 0xffffffff, &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);
+ }
+
+ }
+
+ return result;
+}
+
+
+static int getBadblock(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+
+ char *desc = "Get nvme bad block number.";
+ int result=0, fd;
+
+ OPT_ARGS(opts) = {
+
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ 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(fd,&nvmecmd);
+ if(!result) {
+ int badblock = data[0];
+ printf("Transcend NVME badblock count: %d\n",badblock);
+ }
+
+ return result;
+}
diff --git a/plugins/transcend/transcend-nvme.h b/plugins/transcend/transcend-nvme.h
new file mode 100644
index 0000000..14d62ec
--- /dev/null
+++ b/plugins/transcend/transcend-nvme.h
@@ -0,0 +1,20 @@
+#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"),
+ 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..47b0fdc
--- /dev/null
+++ b/plugins/virtium/virtium-nvme.c
@@ -0,0 +1,1044 @@
+#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 "linux/nvme_ioctl.h"
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+#include "argconfig.h"
+#include "suffix.h"
+
+#define CREATE_CMD
+#include "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_log_page 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 long double int128_to_double(__u8 *data)
+{
+ int i;
+ long double result = 0;
+
+ for (i = 0; i < 16; i++) {
+ result *= 256;
+ result += data[15 - i];
+ }
+ return result;
+}
+
+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;
+
+ curlocale = setlocale(LC_ALL, NULL);
+ templocale = strdup(curlocale);
+
+ if (NULL == templocale)
+ printf("Cannot malloc buffer\n");
+
+ setlocale(LC_ALL, "C");
+
+ long long int lba = 1 << smart->raw_ns.lbaf[(smart->raw_ns.flbas & 0x0f)].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;%0.Lf;", int128_to_double(smart->raw_smart.data_units_read));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Data_Units_Written;%0.Lf;", int128_to_double(smart->raw_smart.data_units_written));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Host_Read_Commands;%0.Lf;", int128_to_double(smart->raw_smart.host_reads));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Host_Write_Commands;%0.Lf;", int128_to_double(smart->raw_smart.host_writes));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Controller_Busy_Time;%0.Lf;", int128_to_double(smart->raw_smart.ctrl_busy_time));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Power_Cycles;%0.Lf;", int128_to_double(smart->raw_smart.power_cycles));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Power_On_Hours;%0.Lf;", int128_to_double(smart->raw_smart.power_on_hours));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Unsafe_Shutdowns;%0.Lf;", int128_to_double(smart->raw_smart.unsafe_shutdowns));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Media_Errors;%0.Lf;", int128_to_double(smart->raw_smart.media_errors));
+ strcat(text, tempbuff);
+ snprintf(tempbuff, sizeof(tempbuff), "Num_Err_Log_Entries;%0.Lf;", int128_to_double(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;
+ char filename[256] = "";
+ int ret = 0;
+ int nsid = 0;
+
+ memset(smart.path, 0, sizeof(smart.path));
+ strcpy(smart.path, path);
+ if(NULL == cfg->output_file)
+ strcpy(filename, vt_default_log_file_name);
+ else
+ strcpy(filename, cfg->output_file);
+
+ smart.time_stamp = time(NULL);
+ nsid = nvme_get_nsid(fd);
+
+ if (nsid <= 0) {
+ printf("Cannot read namespace-id\n");
+ return -1;
+ }
+
+ ret = nvme_identify_ns(fd, nsid, 0, &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_smart_log(fd, NVME_NSID_ALL, &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;
+ char filename[256] = "";
+ int ret = 0;
+
+ vt_initialize_header_buffer(&header);
+ strcpy(header.path, path);
+
+ if (NULL == cfg->test_name)
+ strcpy(header.test_name, DEFAULT_TEST_NAME);
+ else
+ strcpy(header.test_name, cfg->test_name);
+
+ if(NULL == cfg->output_file)
+ strcpy(filename, vt_default_log_file_name);
+ else
+ strcpy(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_fw_log(fd, &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].idle_scale;
+ 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].active_work_scale;
+ 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 *NVSCCtable[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->nvscc;
+ printf(" \"NVM Vendor Specific Command Configuration\":{\n");
+ vt_convert_data_buffer_to_hex_string(&buf[530], 1, true, s);
+ printf(" \"Value\":\"%sh\",\n", s);
+ vt_build_identify_lv2(temp, 0, 1, NVSCCtable, true);
+
+ vt_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 err = 0;
+ int fd, ret;
+ 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 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)
+ strcpy(path, argv[1]);
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ printf("Error parse and open (fd = %d)\n", fd);
+ return (fd);
+ }
+
+ 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(fd, path, &cfg);
+ if (ret) {
+ err = EINVAL;
+ close(fd);
+ 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(fd, 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);
+ }
+
+ close (fd);
+ return (err);
+}
+
+static int vt_show_identify(int argc, char **argv, struct command *cmd, struct plugin *plugin)
+{
+ int err = 0;
+ int fd ,ret;
+ struct nvme_id_ctrl ctrl;
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0) {
+ printf("Error parse and open (fd = %d)\n", fd);
+ return (fd);
+ }
+
+ ret = nvme_identify_ctrl(fd, &ctrl);
+ if (ret) {
+ printf("Cannot read identify device\n");
+ close (fd);
+ return (-1);
+ }
+
+ vt_process_string(ctrl.sn, sizeof(ctrl.sn));
+ vt_process_string(ctrl.mn, sizeof(ctrl.mn));
+ vt_parse_detail_identify(&ctrl);
+
+ close(fd);
+ return (err);
+}
diff --git a/plugins/virtium/virtium-nvme.h b/plugins/virtium/virtium-nvme.h
new file mode 100644
index 0000000..b95c910
--- /dev/null
+++ b/plugins/virtium/virtium-nvme.h
@@ -0,0 +1,21 @@
+#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"),
+ 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..0cebe3f
--- /dev/null
+++ b/plugins/wdc/wdc-nvme.c
@@ -0,0 +1,5663 @@
+/*
+ * 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>
+ */
+#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 "linux/nvme_ioctl.h"
+
+#include "common.h"
+#include "nvme.h"
+#include "nvme-print.h"
+#include "nvme-ioctl.h"
+#include "plugin.h"
+#include "json.h"
+
+#include "argconfig.h"
+#include "suffix.h"
+#include <sys/ioctl.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
+
+/* 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_ZN440_DEV_ID 0x2600
+#define WDC_NVME_SN440_DEV_ID 0x2610
+#define WDC_NVME_SN7GC_DEV_ID 0x2700
+#define WDC_NVME_SN7GC_DEV_ID_1 0x2701
+#define WDC_NVME_SN7GC_DEV_ID_2 0x2702
+#define WDC_NVME_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_SN720_DEV_ID 0x5002
+#define WDC_NVME_SN730A_DEV_ID 0x5006
+#define WDC_NVME_SN730B_DEV_ID 0x3714
+#define WDC_NVME_SN730B_DEV_ID_1 0x3734
+#define WDC_NVME_SN340_DEV_ID 0x500d
+
+#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_DRIVE_ESSENTIALS 0x0000000100000000
+#define WDC_DRIVE_CAP_DUI_DATA 0x0000000200000000
+#define WDC_SN730B_CAP_VUC_LOG 0x0000000400000000
+#define WDC_DRIVE_CAP_SN340_DUI 0x0000000800000000
+#define WDC_DRIVE_CAP_SMART_LOG_MASK (WDC_DRIVE_CAP_C1_LOG_PAGE | WDC_DRIVE_CAP_CA_LOG_PAGE | \
+ WDC_DRIVE_CAP_D0_LOG_PAGE)
+
+/* 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_0x1004 0x1004
+#define WDC_CUSTOMER_ID_0x1005 0x1005
+
+/* 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
+
+/* 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
+
+/* 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
+
+/* 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_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 0x9C
+
+/* C0 EOL Status Log Page */
+#define WDC_NVME_GET_EOL_STATUS_LOG_OPCODE 0xC0
+#define WDC_NVME_EOL_STATUS_LOG_LEN 0x200
+
+/* 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
+
+/* 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_C2 WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE
+#define WDC_LOG_ID_C4 0xC4
+#define WDC_LOG_ID_C5 0xC5
+#define WDC_LOG_ID_C6 0xC6
+#define WDC_LOG_ID_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_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
+
+/* 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_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
+
+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;
+
+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(int fd, 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(int fd, __u8 opcode, __u32 cdw12);
+static int wdc_do_dump(int fd, __u32 opcode,__u32 data_len,
+ __u32 cdw12, char *file, __u32 xfer_size);
+static int wdc_do_crash_dump(int fd, char *file, int type);
+static int wdc_crash_dump(int fd, 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(int fd, 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(int fd, __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(int fd, 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(int fd, 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(int fd, __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(int fd, char *file, int log_id);
+static int wdc_save_reason_id(int fd, __u8 *rsn_ident, int size);
+static int wdc_clear_reason_id(int fd);
+static int wdc_dump_telemetry_hdr(int fd, int log_id, struct nvme_telemetry_log_page_hdr *log_hdr);
+static int wdc_log_page_directory(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+static int wdc_do_drive_info(int fd, __u32 *result);
+static int wdc_vs_drive_info(int argc, char **argv, struct command *command,
+ struct plugin *plugin);
+
+/* 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;
+};
+
+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];
+};
+
+/* 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 wdc_bd_ca_log_format {
+ __u8 field_id;
+ __u8 reserved1[2];
+ __u8 normalized_value;
+ __u8 reserved2;
+ __u8 raw_value[7];
+};
+
+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 */
+};
+
+/* 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[444];
+};
+
+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];
+};
+
+#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 long double int128_to_double(__u8 *data)
+{
+ int i;
+ long double result = 0;
+
+ for (i = 0; i < 16; i++) {
+ result *= 256;
+ result += data[15 - i];
+ }
+ return result;
+}
+
+static int wdc_get_pci_ids(uint32_t *device_id, uint32_t *vendor_id)
+{
+ int fd, ret = -1;
+ char *block, path[512], *id;
+
+ id = calloc(1, 32);
+ if (!id) {
+ fprintf(stderr, "ERROR : WDC : %s : calloc failed\n", __func__);
+ return -1;
+ }
+
+ block = nvme_char_from_block((char *)devicename);
+
+ /* read the vendor ID from sys fs */
+ sprintf(path, "/sys/class/nvme/%s/device/vendor", block);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ sprintf(path, "/sys/class/misc/%s/device/vendor", block);
+ fd = open(path, O_RDONLY);
+ }
+ if (fd < 0) {
+ fprintf(stderr, "ERROR : WDC : %s : Open vendor file failed\n", __func__);
+ ret = -1;
+ goto free_id;
+ }
+
+ ret = read(fd, id, 32);
+ if (ret < 0) {
+ fprintf(stderr, "%s: Read of pci vendor id failed\n", __func__);
+ ret = -1;
+ goto close_fd;
+ } else {
+ if (id[strlen(id) - 1] == '\n')
+ id[strlen(id) - 1] = '\0';
+
+ /* convert the device id string to an int */
+ *vendor_id = (int)strtol(&id[2], NULL, 16);
+ ret = 0;
+ }
+
+ /* read the device ID from sys fs */
+ sprintf(path, "/sys/class/nvme/%s/device/device", block);
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ sprintf(path, "/sys/class/misc/%s/device/device", block);
+ fd = open(path, O_RDONLY);
+ }
+ if (fd < 0) {
+ fprintf(stderr, "ERROR : WDC : %s : Open device file failed\n", __func__);
+ ret = -1;
+ goto close_fd;
+ }
+
+ ret = read(fd, id, 32);
+ if (ret < 0) {
+ fprintf(stderr, "%s: Read of pci device id failed\n", __func__);
+ ret = -1;
+ } else {
+ if (id[strlen(id) - 1] == '\n')
+ id[strlen(id) - 1] = '\0';
+
+ /* convert the device id string to an int */
+ *device_id = strtol(&id[2], NULL, 16);
+ ret = 0;
+ }
+
+close_fd:
+ close(fd);
+free_id:
+ free(block);
+ free(id);
+ return ret;
+}
+
+static bool wdc_check_device(int fd)
+{
+ int ret;
+ bool supported;
+ uint32_t read_device_id, read_vendor_id;
+
+ ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id);
+ if (ret < 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 __u64 wdc_get_drive_capabilities(int fd) {
+ int ret;
+ uint32_t read_device_id, read_vendor_id;
+ __u64 capabilities = 0;
+
+ ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id);
+ if (ret < 0)
+ 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);
+ 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);
+
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+ /* verify the 0xC1 log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_ADD_LOG_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_C1_LOG_PAGE;
+ break;
+ 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(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+ /* verify the 0xD0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
+ 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_SN840_DEV_ID:
+ /* FALLTHRU */
+ case WDC_NVME_SN840_DEV_ID_1:
+ /* FALLTHRU */
+ case WDC_NVME_ZN440_DEV_ID:
+ /* FALLTHRU */
+ case WDC_NVME_SN440_DEV_ID:
+ /* FALLTHRU */
+ case WDC_NVME_SN7GC_DEV_ID:
+ case WDC_NVME_SN7GC_DEV_ID_1:
+ case WDC_NVME_SN7GC_DEV_ID_2:
+ capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG |
+ WDC_DRIVE_CAP_DRIVE_STATUS | WDC_DRIVE_CAP_CLEAR_ASSERT |
+ WDC_DRIVE_CAP_RESIZE | WDC_DRIVE_CAP_CLEAR_PCIE |
+ WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY | WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY |
+ WDC_DRVIE_CAP_DISABLE_CTLR_TELE_LOG | WDC_DRIVE_CAP_REASON_ID |
+ WDC_DRIVE_CAP_LOG_PAGE_DIR | WDC_DRIVE_CAP_INFO);
+
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_CA_LOG_PAGE;
+
+ /* verify the 0xD0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_VU_SMART_LOG_OPCODE) == true)
+ capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
+ break;
+ case WDC_NVME_SN730B_DEV_ID:
+ /* FALLTHRU */
+ case WDC_NVME_SN730B_DEV_ID_1:
+ capabilities = WDC_SN730B_CAP_VUC_LOG;
+ 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:
+ capabilities = WDC_DRIVE_CAP_DUI_DATA;
+ 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_DATA | WDC_DRIVE_CAP_NAND_STATS;
+ break;
+ case WDC_NVME_SN340_DEV_ID:
+ capabilities = WDC_DRIVE_CAP_SN340_DUI;
+ break;
+ default:
+ capabilities = 0;
+ }
+ break;
+ default:
+ capabilities = 0;
+ }
+
+ return capabilities;
+}
+
+static int wdc_get_serial_name(int fd, 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(fd, &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));
+ 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));
+ return -1;
+ }
+
+ if (fsync(fd) < 0) {
+ fprintf(stderr, "ERROR : WDC : fsync : %s\n", strerror(errno));
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+static bool get_dev_mgment_cbs_data(int fd, __u8 log_id, void **cbs_data)
+{
+ int ret = -1;
+ __u8* data;
+ struct wdc_c2_log_page_header *hdr_ptr;
+ struct wdc_c2_log_subpage_header *sph;
+ __u32 length = 0;
+ bool found = false;
+
+ *cbs_data = NULL;
+
+ 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 */
+ ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE,
+ false, WDC_C2_LOG_BUF_LEN, data);
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : Unable to get C2 Log Page length, ret = 0x%x\n", ret);
+ goto end;
+ }
+
+ hdr_ptr = (struct wdc_c2_log_page_header *)data;
+
+ if (le32_to_cpu(hdr_ptr->length) > WDC_C2_LOG_BUF_LEN) {
+ /* Log Page buffer too small, free and reallocate the necessary size */
+ free(data);
+ data = calloc(le32_to_cpu(hdr_ptr->length), sizeof(__u8));
+ if (data == NULL) {
+ fprintf(stderr, "ERROR : WDC : malloc : %s\n", strerror(errno));
+ return false;
+ }
+ }
+
+ ret = nvme_get_log(fd, 0xFFFFFFFF, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE,
+ false, le32_to_cpu(hdr_ptr->length), data);
+ /* parse the data until the List of log page ID's is found */
+ if (ret) {
+ fprintf(stderr, "ERROR : WDC : Unable to read C2 Log Page data, ret = 0x%x\n", ret);
+ goto end;
+ }
+
+ length = sizeof(struct wdc_c2_log_page_header);
+ hdr_ptr = (struct wdc_c2_log_page_header *)data;
+
+ while (length < le32_to_cpu(hdr_ptr->length)) {
+ sph = (struct wdc_c2_log_subpage_header *)(data + length);
+
+ if (le32_to_cpu(sph->entry_id) == log_id) {
+ *cbs_data = (void *)&sph->data;
+ found = true;
+ break;
+ }
+ length += le32_to_cpu(sph->length);
+ }
+
+end:
+ free(data);
+ return found;
+}
+
+static bool wdc_nvme_check_supported_log_page(int fd, __u8 log_id)
+{
+ int i;
+ bool found = false;
+ struct wdc_c2_cbs_data *cbs_data = NULL;
+
+ if (get_dev_mgment_cbs_data(fd, 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
+ } 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(int fd, __le32 *ret_data,
+ __u8 log_id)
+{
+ __u32 *cbs_data = NULL;
+
+ if (get_dev_mgment_cbs_data(fd, log_id, (void *)&cbs_data)) {
+ if (cbs_data != NULL) {
+ memcpy((void *)ret_data, (void *)cbs_data, 4);
+ return true;
+ }
+ }
+
+ *ret_data = 0;
+ return false;
+}
+
+static int wdc_do_clear_dump(int fd, __u8 opcode, __u32 cdw12)
+{
+ int ret;
+ struct nvme_admin_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd));
+ admin_cmd.opcode = opcode;
+ admin_cmd.cdw12 = cdw12;
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+ if (ret != 0) {
+ fprintf(stdout, "ERROR : WDC : Crash dump erase failed\n");
+ }
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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_admin_cmd admin_cmd;
+
+ l = (struct wdc_log_size *) buf;
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_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);
+ if (ret != 0) {
+ l->log_size = 0;
+ ret = -1;
+ fprintf(stderr, "ERROR : WDC : reading dump length failed\n");
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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_admin_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_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);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : reading dump length failed\n");
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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_admin_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_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);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : reading DUI data failed\n");
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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_admin_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_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 = (__u32)(offset & 0x00000000FFFFFFFF);
+ admin_cmd.cdw13 = (__u32)(offset >> 32);
+ 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);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : reading DUI data V2 failed\n");
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ }
+
+ return ret;
+}
+
+static int wdc_do_dump(int fd, __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_admin_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_admin_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(fd, &admin_cmd);
+ if (ret != 0) {
+ fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n",
+ __func__, nvme_status_to_string(ret), ret);
+ fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%x, offset = 0x%x, addr = 0x%lx\n",
+ __func__, i, admin_cmd.data_len, curr_data_offset, (long unsigned int)admin_cmd.addr);
+ break;
+ }
+
+ if ((curr_data_offset + xfer_size) <= data_len)
+ curr_data_len = xfer_size;
+ else
+ curr_data_len = data_len - curr_data_offset; /* last transfer */
+
+ curr_data_offset += curr_data_len;
+ 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) {
+ fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), 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_admin_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_admin_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);
+ if (ret != 0) {
+ fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret);
+ fprintf(stderr, "%s: ERROR : WDC : Get chunk %d, size = 0x%x, offset = 0x%x, addr = 0x%lx\n",
+ __func__, i, admin_cmd.data_len, curr_data_offset, (long unsigned int)admin_cmd.addr);
+ break;
+ }
+
+ log_size -= xfer_size;
+ curr_data_offset += xfer_size;
+ i++;
+ }
+
+ if (ret == 0) {
+ fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret);
+ } else {
+ fprintf(stderr, "%s: FAILURE: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), 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(int fd, char *file, __u32 bs, int type, int data_area)
+{
+ struct nvme_telemetry_log_page_hdr *hdr;
+ size_t full_size, offset = WDC_TELEMETRY_HEADER_LENGTH;
+ int err = 0, output;
+ void *page_log;
+ __u32 host_gen = 1;
+ int ctrl_init = 0;
+ __u32 result;
+ void *buf = NULL;
+
+
+ if (type == WDC_TELEMETRY_TYPE_HOST) {
+ host_gen = 1;
+ ctrl_init = 0;
+ } else if (type == WDC_TELEMETRY_TYPE_CONTROLLER) {
+ /* Verify the Controller Initiated Option is enabled */
+ err = nvme_get_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0, 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__);
+ err = -EINVAL;
+ goto close_fd;
+ }
+ } else {
+ fprintf(stderr, "ERROR : WDC: Get telemetry option feature failed. NVMe Status:%s(%x)\n",
+ nvme_status_to_string(err), err);
+ err = -EPERM;
+ goto close_fd;
+ }
+ } else {
+ fprintf(stderr, "%s: Invalid type parameter; type = %d\n", __func__, type);
+ err = -EINVAL;
+ goto close_fd;
+ }
+
+ if (!file) {
+ fprintf(stderr, "%s: Please provide an output file!\n", __func__);
+ err = -EINVAL;
+ goto close_fd;
+ }
+
+ hdr = malloc(bs);
+ page_log = malloc(bs);
+ if (!hdr || !page_log) {
+ fprintf(stderr, "%s: Failed to allocate 0x%x bytes for log: %s\n",
+ __func__, bs, strerror(errno));
+ err = -ENOMEM;
+ goto free_mem;
+ }
+ memset(hdr, 0, bs);
+
+ 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));
+ err = output;
+ goto free_mem;
+ }
+
+ err = nvme_get_telemetry_log(fd, hdr, host_gen, ctrl_init, WDC_TELEMETRY_HEADER_LENGTH, 0);
+ if (err < 0)
+ perror("get-telemetry-log");
+ else if (err > 0) {
+ nvme_show_status(err);
+ fprintf(stderr, "%s: Failed to acquire telemetry header!\n", __func__);
+ goto close_output;
+ }
+
+ err = write(output, (void *) hdr, WDC_TELEMETRY_HEADER_LENGTH);
+ if (err != WDC_TELEMETRY_HEADER_LENGTH) {
+ fprintf(stderr, "%s: Failed to flush header data to file!, err = %d\n", __func__, err);
+ goto close_output;
+ }
+
+ switch (data_area) {
+ case 1:
+ full_size = (le16_to_cpu(hdr->dalb1) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
+ break;
+ case 2:
+ full_size = (le16_to_cpu(hdr->dalb2) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
+ break;
+ case 3:
+ full_size = (le16_to_cpu(hdr->dalb3) * WDC_TELEMETRY_BLOCK_SIZE) + WDC_TELEMETRY_HEADER_LENGTH;
+ break;
+ default:
+ fprintf(stderr, "%s: Invalid data area requested, data area = %d\n", __func__, data_area);
+ err = -EINVAL;
+ goto close_output;
+ }
+
+ /*
+ * Continuously pull data until the offset hits the end of the last
+ * block.
+ */
+ while (offset < full_size) {
+ if ((full_size - offset) < bs)
+ bs = (full_size - offset);
+
+
+ err = nvme_get_telemetry_log(fd, page_log, 0, ctrl_init, bs, offset);
+ if (err < 0) {
+ perror("get-telemetry-log");
+ break;
+ } else if (err > 0) {
+ nvme_show_status(err);
+ fprintf(stderr, "%s: Failed to acquire full telemetry log!\n", __func__);
+ nvme_show_status(err);
+ break;
+ }
+
+ err = write(output, (void *) page_log, bs);
+ if (err != bs) {
+ fprintf(stderr, "%s: Failed to flush telemetry data to file!, err = %d\n", __func__, err);
+ break;
+ }
+ err = 0;
+ offset += bs;
+ }
+
+close_output:
+ close(output);
+free_mem:
+ free(hdr);
+ free(page_log);
+close_fd:
+ close(fd);
+
+ return err;
+
+}
+
+static int wdc_do_cap_diag(int fd, 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(fd, 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(fd, 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(fd, 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;
+ __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 : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret);
+ goto out;
+ }
+
+ /* Check the Log Header version */
+ 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_v3->hdr_version >= 0x03)
+ fprintf(stderr, "INFO : WDC : DUI Product ID = %c\n", 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%lx\n",
+ __func__, log_hdr_v3->log_section[j].data_area_id, (unsigned int)log_hdr_v3->log_section[j].section_size, (long unsigned int)log_size);
+ }
+ else {
+ if (verbose)
+ fprintf(stderr, "%s: break, total size = 0x%lx\n", __func__, (long unsigned int)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%lx\n",
+ __func__, strerror(errno), (long 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%lx, offset = 0x%lx, addr = 0x%lx\n",
+ __func__, i, (long unsigned int)total_size, (long unsigned int)curr_data_offset, (long unsigned int)buffer_addr);
+ fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret);
+ break;
+ }
+
+ /* write the dump data into the file */
+ err = write(output, (void *)buffer_addr, xfer_size_long);
+ if (err != xfer_size_long) {
+ fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%lx\n",
+ __func__, i, err, (long unsigned int)xfer_size_long);
+ goto free_mem;
+ }
+
+ curr_data_offset += xfer_size_long;
+ i++;
+ }
+ }
+ } else {
+ __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%lx, offset = 0x%x, addr = %p\n",
+ __func__, i, (long unsigned int)log_size, curr_data_offset, buffer_addr);
+ fprintf(stderr, "%s: ERROR : WDC : NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret);
+ break;
+ }
+
+ /* write the dump data into the file */
+ err = write(output, (void *)buffer_addr, xfer_size);
+ if (err != xfer_size) {
+ fprintf(stderr, "%s: ERROR : WDC : Failed to flush DUI data to file! chunk %d, err = 0x%x, xfer_size = 0x%x\n",
+ __func__, i, err, xfer_size);
+ goto free_mem;
+ }
+
+ curr_data_offset += xfer_size;
+ i++;
+ }
+ }
+ }
+
+ fprintf(stderr, "%s: NVMe Status:%s(%x)\n", __func__, nvme_status_to_string(ret), ret);
+ if (verbose)
+ fprintf(stderr, "INFO : WDC : Capture Device Unit Info log, length = 0x%lx\n", (long unsigned int)total_size);
+
+ 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)
+{
+ char *desc = "Capture Diagnostics Log.";
+ char *file = "Output file pathname.";
+ char *size = "Data retrieval transfer size.";
+ char f[PATH_MAX] = {0};
+ __u32 xfer_size = 0;
+ int fd;
+ __u64 capabilities = 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (cfg.file != NULL)
+ strncpy(f, cfg.file, PATH_MAX - 1);
+ if (cfg.xfer_size != 0)
+ xfer_size = cfg.xfer_size;
+ if (wdc_get_serial_name(fd, f, PATH_MAX, "cap_diag") == -1) {
+ fprintf(stderr, "ERROR : WDC: failed to generate file name\n");
+ return -1;
+ }
+ if (cfg.file == NULL)
+ snprintf(f + strlen(f), PATH_MAX, "%s", ".bin");
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_CAP_DIAG) == WDC_DRIVE_CAP_CAP_DIAG)
+ return wdc_do_cap_diag(fd, f, xfer_size, 0, 0);
+
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ return 0;
+}
+
+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_admin_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_admin_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);
+ 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_admin_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_admin_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);
+ 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) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ goto free_buf;
+ }
+ ret = wdc_do_get_sn730_log_len(fd, &key_log_len, SN730_GET_KEY_LOG_LENGTH);
+ if (ret) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ goto free_buf;
+ }
+ ret = wdc_do_get_sn730_log_len(fd, &core_dump_log_len, SN730_GET_COREDUMP_LOG_LENGTH);
+ if (ret) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ goto free_buf;
+ }
+ ret = wdc_do_get_sn730_log_len(fd, &extended_log_len, SN730_GET_EXTENDED_LOG_LENGTH);
+ if (ret) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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) {
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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, and SN840 devices.";
+ char *file_size = "Output file size. Currently only supported on the SN340 device.";
+ char *offset = "Output file data offset. Currently only supported on the SN340 device.";
+ char *type = "Telemetry type - NONE, HOST, or CONTROLLER. Currently only supported on the SN640 and SN840 devices.";
+ char *verbose = "Display more debug messages.";
+ char f[PATH_MAX] = {0};
+ char fileSuffix[PATH_MAX] = {0};
+ __u32 xfer_size = 0;
+ int fd;
+ int telemetry_type = 0, telemetry_data_area = 0;
+ UtilsTimeInfo timeInfo;
+ __u8 timeStamp[MAX_PATH_LEN];
+ __u64 capabilities = 0;
+
+ struct config {
+ char *file;
+ __u32 xfer_size;
+ int data_area;
+ __u64 file_size;
+ __u64 offset;
+ char *type;
+ int verbose;
+ };
+
+ struct config cfg = {
+ .file = NULL,
+ .xfer_size = 0x10000,
+ .data_area = 3,
+ .file_size = 0,
+ .offset = 0,
+ .type = NULL,
+ .verbose = 0,
+ };
+
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!wdc_check_device(fd))
+ return -1;
+ if (cfg.xfer_size != 0)
+ xfer_size = cfg.xfer_size;
+ else {
+ fprintf(stderr, "ERROR : WDC : Invalid length\n");
+ return -1;
+ }
+
+ 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));
+ return -1;
+ }
+ 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);
+
+ if (wdc_get_serial_name(fd, f, PATH_MAX, fileSuffix) == -1) {
+ fprintf(stderr, "ERROR : WDC: failed to generate file name\n");
+ return -1;
+ }
+ }
+
+ if (cfg.file == NULL)
+ snprintf(f + strlen(f), PATH_MAX, "%s", ".bin");
+ fprintf(stderr, "%s: filename = %s\n", __func__, f);
+
+ if (cfg.data_area > 5 || cfg.data_area == 0) {
+ fprintf(stderr, "ERROR : WDC: Data area must be 1-5\n");
+ return -1;
+ }
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_INTERNAL_LOG) == WDC_DRIVE_CAP_INTERNAL_LOG) {
+ 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");
+ return -1;
+ }
+
+ return wdc_do_cap_diag(fd, f, xfer_size, telemetry_type, telemetry_data_area);
+ }
+ if ((capabilities & WDC_DRIVE_CAP_SN340_DUI) == WDC_DRIVE_CAP_SN340_DUI) {
+ /* FW requirement - xfer size must be 256k for data area 4 */
+ if (cfg.data_area >= 4)
+ xfer_size = 0x40000;
+ return wdc_do_cap_dui(fd, f, xfer_size, cfg.data_area, cfg.verbose, cfg.file_size, cfg.offset);
+ }
+ if ((capabilities & WDC_DRIVE_CAP_DUI_DATA) == WDC_DRIVE_CAP_DUI_DATA)
+ return wdc_do_cap_dui(fd, f, xfer_size, WDC_NVME_DUI_MAX_DATA_AREA, cfg.verbose, 0, 0);
+ if ((capabilities & WDC_SN730B_CAP_VUC_LOG) == WDC_SN730B_CAP_VUC_LOG)
+ return wdc_do_sn730_get_and_tar(fd, f);
+
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ return -1;
+}
+
+static int wdc_do_crash_dump(int fd, 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(fd,
+ 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(fd,
+ opcode,
+ crash_dump_length,
+ cdw12,
+ file,
+ crash_dump_length);
+
+ if (ret == 0)
+ ret = wdc_do_clear_dump(fd, WDC_NVME_CLEAR_DUMP_OPCODE, cdw12_clear);
+ }
+ return ret;
+}
+
+static int wdc_crash_dump(int fd, char *file, int type)
+{
+ char f[PATH_MAX] = {0};
+ const char *dump_type;
+
+ 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";
+
+ if (wdc_get_serial_name(fd, f, PATH_MAX, dump_type) == -1) {
+ fprintf(stderr, "ERROR : WDC : failed to generate file name\n");
+ return -1;
+ }
+ return wdc_do_crash_dump(fd, f, type);
+}
+
+static int wdc_do_drive_log(int fd, char *file)
+{
+ int ret;
+ __u8 *drive_log_data;
+ __u32 drive_log_length;
+ struct nvme_admin_cmd admin_cmd;
+
+ ret = wdc_dump_length(fd, 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_admin_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(fd, &admin_cmd);
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret),
+ 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};
+ int fd;
+ int ret;
+ __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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!wdc_check_device(fd))
+ return -1;
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ 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);
+ }
+ if (wdc_get_serial_name(fd, f, PATH_MAX, "drive_log") == -1) {
+ fprintf(stderr, "ERROR : WDC : failed to generate file name\n");
+ return -1;
+ }
+ ret = wdc_do_drive_log(fd, f);
+ }
+ 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.";
+ int fd, ret;
+ __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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!wdc_check_device(fd))
+ return -1;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ 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(fd, cfg.file, WDC_NVME_CRASH_DUMP_TYPE);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : failed to read crash dump\n");
+ }
+ }
+ 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.";
+ int fd;
+ int ret;
+ __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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!wdc_check_device(fd))
+ return -1;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ 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(fd, cfg.file, WDC_NVME_PFAIL_DUMP_TYPE);
+ if (ret != 0) {
+ fprintf(stderr, "ERROR : WDC : failed to read pfail crash dump\n");
+ }
+ }
+
+ 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.";
+ char *err_str;
+ int fd, ret;
+ struct nvme_passthru_cmd admin_cmd;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ err_str = "";
+ memset(&admin_cmd, 0, sizeof (admin_cmd));
+ admin_cmd.opcode = WDC_NVME_PURGE_CMD_OPCODE;
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!wdc_check_device(fd))
+ return -1;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+ 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);
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ 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.";
+ int fd, ret;
+ __u8 output[WDC_NVME_PURGE_MONITOR_DATA_LEN];
+ double progress_percent;
+ struct nvme_passthru_cmd admin_cmd;
+ struct wdc_nvme_purge_monitor_data *mon;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ memset(output, 0, sizeof (output));
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_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;
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!wdc_check_device(fd))
+ return -1;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+ 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);
+ }
+ }
+
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ 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 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(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;
+
+ if (bd_data->field_id == 0x00) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" CA Log Page values :- \n");
+ printf(" Program fail counts %20"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ printf(" %% Remaining of allowable program fails %3"PRIu8"\n",
+ bd_data->normalized_value);
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x01) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Erase fail count %20"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ printf(" %% Remaining of allowable erase fails %3"PRIu8"\n",
+ bd_data->normalized_value);
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x02) {
+ word_raw = (__u16*)bd_data->raw_value;
+ printf(" Min erase cycles %10"PRIu16"\n",
+ le16_to_cpu(*word_raw));
+ word_raw = (__u16*)&bd_data->raw_value[2];
+ printf(" Max erase cycles %10"PRIu16"\n",
+ le16_to_cpu(*word_raw));
+ word_raw = (__u16*)&bd_data->raw_value[4];
+ printf(" Ave erase cycles %10"PRIu16"\n",
+ le16_to_cpu(*word_raw));
+ printf(" Wear Leveling Normalized %3"PRIu8"\n",
+ bd_data->normalized_value);
+
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x03) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" End to end error detection count %20"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x04) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Crc error count %20"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x05) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Timed workload media error %20.3f\n",
+ safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 1024.0));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x06) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Timed workload host reads %% %3"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00000000000000FF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x07) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Timed workload timer %20"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x08) {
+ byte_raw = (__u8*)bd_data->raw_value;
+ printf(" Throttle status %% %10"PRIu16"\n",
+ *byte_raw);
+ dword_raw = (__u32*)&bd_data->raw_value[1];
+ printf(" Throttling event counter %10"PRIu16"\n",
+ le32_to_cpu(*dword_raw));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x09) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Retry buffer overflow count %20"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0A) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Pll lock loss count %20"PRIu64"\n",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0B) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Nand bytes written (32mb) %20.0f\n",
+ safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ raw = (__u64*)bd_data->raw_value;
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0C) {
+ raw = (__u64*)bd_data->raw_value;
+ printf(" Host bytes written (32mb) %20.0f\n",
+ safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ raw = (__u64*)bd_data->raw_value;
+ } 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;
+ json_object_add_value_int(root, "Program fail counts",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ json_object_add_value_int(root, "% Remaining of allowable program fails",
+ bd_data->normalized_value);
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x01) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_int(root, "Erase fail count",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ json_object_add_value_int(root, "% Remaining of allowable erase fails",
+ bd_data->normalized_value);
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x02) {
+ word_raw = (__u16*)bd_data->raw_value;
+ json_object_add_value_int(root, "Min erase cycles", le16_to_cpu(*word_raw));
+ word_raw = (__u16*)&bd_data->raw_value[2];
+ json_object_add_value_int(root, "Max erase cycles", le16_to_cpu(*word_raw));
+ word_raw = (__u16*)&bd_data->raw_value[4];
+ json_object_add_value_int(root, "Ave erase cycles", le16_to_cpu(*word_raw));
+ json_object_add_value_int(root, "Wear Leveling Normalized", bd_data->normalized_value);
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x03) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_int(root, "End to end error detection count",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x04) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_int(root, "Crc error count",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x05) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_float(root, "Timed workload media error",
+ 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;
+ json_object_add_value_int(root, "Timed workload host reads %",
+ le64_to_cpu(*raw & 0x00000000000000FF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x07) {
+ raw = (__u64*)bd_data->raw_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;
+ json_object_add_value_int(root, "Throttle status %", *byte_raw);
+ dword_raw = (__u32*)&bd_data->raw_value[1];
+ json_object_add_value_int(root, "Throttling event counter", le32_to_cpu(*dword_raw));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x09) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_int(root, "Retry buffer overflow count",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0A) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_int(root, "Pll lock loss count",
+ le64_to_cpu(*raw & 0x00FFFFFFFFFFFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0B) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_float(root, "Nand bytes written (32mb)",
+ safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ } else {
+ goto invalid_id;
+ }
+ bd_data++;
+ if (bd_data->field_id == 0x0C) {
+ raw = (__u64*)bd_data->raw_value;
+ json_object_add_value_float(root, "Host bytes written (32mb)",
+ safe_div_fp((*raw & 0x00FFFFFFFFFFFFFF), 0xFFFF));
+ raw = (__u64*)bd_data->raw_value;
+ } else {
+ goto invalid_id;
+ }
+
+ goto done;
+
+ invalid_id:
+ printf(" Invalid Field ID = %d\n", bd_data->field_id);
+
+ done:
+ 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(struct wdc_fw_act_history_log_entry *fw_act_history_entry,
+ int num_entries)
+{
+ int i;
+ char previous_fw[9];
+ char new_fw[9];
+ char commit_action_bin[8];
+ memset((void *)previous_fw, 0, 9);
+ memset((void *)new_fw, 0, 9);
+ memset((void *)commit_action_bin, 0, 8);
+ char *null_fw = "--------";
+
+
+ printf(" Firmware Activate History Log \n");
+ printf(" Power on Hour Power Cycle Previous New \n");
+ printf(" Entry hh:mm:ss Count Firmware Firmware Slot Action Result \n");
+ printf(" ----- -------------- ------------ ---------- ---------- ----- ------ -------\n");
+
+ for (i = 0; i < num_entries; i++) {
+ memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8);
+ if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1)
+ memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8);
+ else
+ memcpy(new_fw, null_fw, 8);
+
+ printf("%5"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->entry_num));
+ printf(" ");
+ printf("%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600),
+ (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60),
+ (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60));
+ printf(" ");
+ printf("%8"PRIu32"", (uint32_t)le32_to_cpu(fw_act_history_entry->power_cycle_count));
+ printf(" ");
+ printf("%s", (char *)previous_fw);
+ printf(" ");
+ printf("%s", (char *)new_fw);
+ printf(" ");
+ printf("%2"PRIu8"", (uint8_t)fw_act_history_entry->slot_number);
+ printf(" ");
+ wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin);
+ printf(" %s", (char *)commit_action_bin);
+ printf(" ");
+ if (le16_to_cpu(fw_act_history_entry->result) == 0)
+ printf("pass");
+ else
+ printf("fail #%d", (uint16_t)le16_to_cpu(fw_act_history_entry->result));
+
+ printf("\n");
+
+ fw_act_history_entry++;
+ }
+}
+
+static void wdc_print_fw_act_history_log_json(struct wdc_fw_act_history_log_entry *fw_act_history_entry,
+ int num_entries)
+{
+ struct json_object *root;
+ int i;
+ char previous_fw[9];
+ char new_fw[9];
+ char commit_action_bin[8];
+ char fail_str[32];
+ char time_str[9];
+ memset((void *)previous_fw, 0, 9);
+ memset((void *)new_fw, 0, 9);
+ memset((void *)commit_action_bin, 0, 8);
+ memset((void *)time_str, 0, 9);
+ memset((void *)fail_str, 0, 11);
+ char *null_fw = "--------";
+
+ root = json_create_object();
+
+ for (i = 0; i < num_entries; i++) {
+ memcpy(previous_fw, (char *)&(fw_act_history_entry->previous_fw_version), 8);
+ if (strlen((char *)&(fw_act_history_entry->new_fw_version)) > 1)
+ memcpy(new_fw, (char *)&(fw_act_history_entry->new_fw_version), 8);
+ else
+ memcpy(new_fw, null_fw, 8);
+
+ json_object_add_value_int(root, "Entry",
+ le32_to_cpu(fw_act_history_entry->entry_num));
+
+ sprintf((char *)time_str, "%02d:%02d:%02d", (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)/3600),
+ (int)((le64_to_cpu(fw_act_history_entry->power_on_seconds)%3600)/60),
+ (int)(le64_to_cpu(fw_act_history_entry->power_on_seconds)%60));
+ json_object_add_value_string(root, "Power on Hour", time_str);
+
+ json_object_add_value_int(root, "Power Cycle Count",
+ le32_to_cpu(fw_act_history_entry->power_cycle_count));
+ json_object_add_value_string(root, "Previous Firmware",
+ previous_fw);
+ json_object_add_value_string(root, "New Firmware",
+ new_fw);
+ json_object_add_value_int(root, "Slot",
+ fw_act_history_entry->slot_number);
+
+ wdc_get_commit_action_bin(fw_act_history_entry->commit_action_type,(char *)&commit_action_bin);
+ json_object_add_value_string(root, "Action", commit_action_bin);
+
+ if (le16_to_cpu(fw_act_history_entry->result) == 0)
+ json_object_add_value_string(root, "Result", "pass");
+ else {
+ sprintf((char *)fail_str, "fail #%d", (int)(le16_to_cpu(fw_act_history_entry->result)));
+ json_object_add_value_string(root, "Result", fail_str);
+ }
+
+ fw_act_history_entry++;
+ }
+
+ json_print_object(root, NULL);
+ printf("\n");
+
+ json_free_object(root);
+}
+
+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(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(bd_data);
+ break;
+ case JSON:
+ wdc_print_bd_ca_log_json(bd_data);
+ break;
+ }
+ 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(struct wdc_fw_act_history_log_entry *fw_act_history_entries,
+ int num_entries,
+ int fmt)
+{
+ if (!fw_act_history_entries) {
+ fprintf(stderr, "ERROR : WDC : Invalid buffer to read fw activate history entries\n");
+ return -1;
+ }
+
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_fw_act_history_log_normal(fw_act_history_entries, num_entries);
+ break;
+ case JSON:
+ wdc_print_fw_act_history_log_json(fw_act_history_entries, num_entries);
+ break;
+ }
+ return 0;
+}
+
+static int wdc_get_ca_log_page(int fd, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ __u32 *cust_id;
+ struct wdc_ssd_ca_perf_stats *perf;
+ uint32_t read_device_id, read_vendor_id;
+
+ if (!wdc_check_device(fd))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ /* verify the 0xCA log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE) == false) {
+ fprintf(stderr, "ERROR : WDC : 0xCA Log Page not supported\n");
+ return -1;
+ }
+
+ if (!get_dev_mgment_cbs_data(fd, WDC_C2_CUSTOMER_ID_ID, (void*)&data)) {
+ fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page entry ID 0x%x not found\n", __func__, WDC_C2_CUSTOMER_ID_ID);
+ return -1;
+ }
+
+ ret = wdc_get_pci_ids(&read_device_id, &read_vendor_id);
+
+ cust_id = (__u32*)data;
+
+ 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(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+ false, WDC_FB_CA_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ 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 = %d\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:
+
+ 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(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+ false, WDC_FB_CA_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ 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)) {
+
+ 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(fd, 0xFFFFFFFF, WDC_NVME_GET_DEVICE_INFO_LOG_OPCODE,
+ false, WDC_BD_CA_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ ret = wdc_print_bd_ca_log(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 = %d\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(int fd, 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(fd))
+ 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(fd, 0x01, WDC_NVME_ADD_LOG_OPCODE, false,
+ WDC_ADD_LOG_BUF_LEN, data);
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ if (ret == 0) {
+ 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_d0_log_page(int fd, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ struct wdc_ssd_d0_smart_log *perf;
+
+ if (!wdc_check_device(fd))
+ return -1;
+ fmt = validate_output_format(format);
+ if (fmt < 0) {
+ fprintf(stderr, "ERROR : WDC : invalid output format\n");
+ return fmt;
+ }
+
+ /* verify the 0xD0 log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, 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(fd, 0xFFFFFFFF, WDC_NVME_GET_VU_SMART_LOG_OPCODE,
+ false, WDC_NVME_VU_SMART_LOG_LEN, data);
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ 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].";
+ int fd;
+ int ret = 0;
+ __u64 capabilities = 0;
+
+ struct config {
+ uint8_t interval;
+ int vendor_specific;
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .interval = 14,
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("interval", 'i', &cfg.interval, interval),
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ 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_CA_LOG_PAGE)) == (WDC_DRIVE_CAP_CA_LOG_PAGE)) {
+ /* Get the CA Log Page */
+ ret = wdc_get_ca_log_page(fd, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the CA Log Page, ret = %d\n", ret);
+ }
+ if ((capabilities & WDC_DRIVE_CAP_C1_LOG_PAGE) == WDC_DRIVE_CAP_C1_LOG_PAGE) {
+ /* Get the C1 Log Page */
+ ret = wdc_get_c1_log_page(fd, cfg.output_format, cfg.interval);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the C1 Log Page, ret = %d\n", ret);
+ }
+ if ((capabilities & WDC_DRIVE_CAP_D0_LOG_PAGE) == WDC_DRIVE_CAP_D0_LOG_PAGE) {
+ /* Get the D0 Log Page */
+ ret = wdc_get_d0_log_page(fd, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the D0 Log Page, ret = %d\n", ret);
+ }
+out:
+ return ret;
+}
+
+static int wdc_clear_pcie_correctable_errors(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Clear PCIE Correctable Errors.";
+ int fd, ret;
+ __u64 capabilities = 0;
+ struct nvme_passthru_cmd admin_cmd;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ if (!wdc_check_device(fd)) {
+ ret = -1;
+ goto out;
+ }
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_CLEAR_PCIE) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ memset(&admin_cmd, 0, sizeof (admin_cmd));
+ admin_cmd.opcode = WDC_NVME_CLEAR_PCIE_CORR_OPCODE;
+ admin_cmd.cdw12 = ((WDC_NVME_CLEAR_PCIE_CORR_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_CLEAR_PCIE_CORR_CMD);
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+out:
+ return ret;
+}
+static int wdc_drive_status(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ char *desc = "Get Drive Status.";
+ int fd;
+ int ret = -1;
+ __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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ 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(fd, 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(fd, &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(fd, &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(fd, &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(fd, &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(fd, &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(fd, &format_corrupt_reason,
+ WDC_C2_FORMAT_CORRUPT_REASON_ID))
+ fprintf(stderr, "ERROR : WDC : Get Format Corrupt Reason Failed\n");
+
+ printf(" Drive Status :- \n");
+ if (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:
+ 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.";
+ int fd;
+ int ret = -1;
+ __le32 assert_status = cpu_to_le32(0xFFFFFFFF);
+ __u64 capabilities = 0;
+ struct nvme_passthru_cmd admin_cmd;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ 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(fd, &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(fd, &admin_cmd);
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ } else
+ fprintf(stderr, "INFO : WDC : No Assert Dump Present\n");
+
+out:
+ return ret;
+}
+
+static int wdc_get_fw_act_history(int fd, char *format)
+{
+ int ret = 0;
+ int fmt = -1;
+ __u8 *data;
+ struct wdc_fw_act_history_log_hdr *fw_act_history_hdr;
+ struct wdc_fw_act_history_log_entry *fw_act_history_entry;
+
+ if (!wdc_check_device(fd))
+ return -1;
+
+ 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(fd, 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(fd, 0xFFFFFFFF, WDC_NVME_GET_FW_ACT_HISTORY_LOG_ID,
+ false, WDC_FW_ACT_HISTORY_LOG_BUF_LEN, data);
+
+ if (strcmp(format, "json"))
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+ if (ret == 0) {
+ /* parse the data */
+ fw_act_history_hdr = (struct wdc_fw_act_history_log_hdr *)(data);
+ fw_act_history_entry = (struct wdc_fw_act_history_log_entry *)(data + sizeof(struct wdc_fw_act_history_log_hdr));
+
+ if (fw_act_history_hdr->num_entries > 0)
+ ret = wdc_print_fw_act_history_log(fw_act_history_entry, fw_act_history_hdr->num_entries, fmt);
+ else
+ fprintf(stderr, "INFO : WDC : No entries found in FW Activate History Log Page\n");
+ } else {
+ fprintf(stderr, "ERROR : WDC : Unable to read FW Activate History Log Page data\n");
+ ret = -1;
+ }
+
+ free(data);
+ return ret;
+}
+
+static int wdc_vs_fw_activate_history(int argc, char **argv, struct command *command,
+ struct plugin *plugin)
+{
+ int fd;
+ int ret = 0;
+ __u64 capabilities = 0;
+ const char *desc = "Retrieve FW activate history table.";
+
+
+ struct config {
+ char *output_format;
+ };
+
+ struct config cfg = {
+ .output_format = "normal",
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_FMT("output-format", 'o', &cfg.output_format, "Output Format: normal|json"),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ if ((capabilities & WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) == WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY) {
+ ret = wdc_get_fw_act_history(fd, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading the FW Activate History, ret = %d\n", ret);
+ } else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ }
+
+ 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.";
+ int fd;
+ int ret = -1;
+ __u64 capabilities = 0;
+ struct nvme_passthru_cmd admin_cmd;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) != WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ goto out;
+ }
+
+ memset(&admin_cmd, 0, sizeof (admin_cmd));
+ admin_cmd.opcode = WDC_NVME_CLEAR_FW_ACT_HIST_OPCODE;
+ admin_cmd.cdw12 = ((WDC_NVME_CLEAR_FW_ACT_HIST_SUBCMD << WDC_NVME_SUBCMD_SHIFT) |
+ WDC_NVME_CLEAR_FW_ACT_HIST_CMD);
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+out:
+ 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.";
+ int fd;
+ int ret = -1;
+ __u64 capabilities = 0;
+ __u32 result;
+ void *buf = NULL;
+
+
+ struct config {
+ int disable;
+ int enable;
+ int status;
+ };
+
+ struct config cfg = {
+ .disable = 0,
+ .enable = 0,
+ .status = 0,
+ };
+
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ 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_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 1,
+ 0, 0, 0, buf, &result);
+
+ wdc_clear_reason_id(fd);
+ }
+ else {
+ if (cfg.enable) {
+ ret = nvme_set_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0,
+ 0, 0, 0, buf, &result);
+ }
+ else if (cfg.status) {
+ ret = nvme_get_feature(fd, 0, WDC_VU_DISABLE_CNTLR_TELEMETRY_OPTION_FEATURE_ID, 0, 0,
+ 4, buf, &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 {
+ fprintf(stderr, "ERROR : WDC: NVMe Status:%s(%x)\n", nvme_status_to_string(ret), 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:
+ return ret;
+}
+
+
+static int wdc_get_serial_and_fw_rev(int fd, 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(fd, &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(int fd, __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(fd, &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(int fd, __u32 fileId, __u16 spiDestn, __u32* logSize)
+{
+ int ret = WDC_STATUS_FAILURE;
+ struct nvme_admin_cmd cmd;
+
+ if(!fd || !logSize )
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ memset(&cmd,0,sizeof(struct nvme_admin_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(fd, &cmd);
+
+ if (!ret && logSize)
+ *logSize = cmd.result;
+ if( ret != WDC_STATUS_SUCCESS)
+ fprintf(stderr, "ERROR : WDC : VUReadSize() failed, status:%s(0x%x)\n", nvme_status_to_string(ret), ret);
+
+ end:
+ return ret;
+}
+
+static int wdc_de_VU_read_buffer(int fd, __u32 fileId, __u16 spiDestn, __u32 offsetInDwords, __u8* dataBuffer, __u32* bufferSize)
+{
+ int ret = WDC_STATUS_FAILURE;
+ struct nvme_admin_cmd cmd;
+ __u32 noOfDwordExpected = 0;
+
+ if(!fd || !dataBuffer || !bufferSize)
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ memset(&cmd,0,sizeof(struct nvme_admin_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(fd, &cmd);
+
+ if( ret != WDC_STATUS_SUCCESS)
+ fprintf(stderr, "ERROR : WDC : VUReadBuffer() failed, status:%s(0x%x)\n", nvme_status_to_string(ret), ret);
+
+ end:
+ return ret;
+}
+
+static int wdc_get_log_dir_max_entries(int fd, __u32* maxNumOfEntries)
+{
+ int ret = WDC_STATUS_FAILURE;
+ __u32 headerPayloadSize = 0;
+ __u8* fileIdOffsetsBuffer = NULL;
+ __u32 fileIdOffsetsBufferSize = 0;
+ __u32 fileNum = 0;
+ __u16 fileOffset = 0;
+
+
+ if (!fd || !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(fd, 0, 5, (__u32*)&headerPayloadSize)))
+ {
+ fprintf(stderr, "ERROR : WDC : %s: Failed to get headerPayloadSize from file directory 0x%x\n",
+ __func__, ret);
+ goto end;
+ }
+
+ 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(fd, 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:
+ if (!fileIdOffsetsBuffer)
+ 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(int fd, 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 (!fd || !directory) {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ ret = wdc_de_VU_read_size(fd, 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(fd, 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(int fd, __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 (!fd || !dataBuffer || !fileSize)
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ goto end;
+ }
+
+ wdc_get_max_transfer_len(fd, &maximumTransferLength);
+
+ /* 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(fd, 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(fd, 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(int fd, 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 (!fd || !binFileName || !filePath)
+ {
+ ret = WDC_STATUS_INVALID_PARAMETER;
+ return ret;
+ }
+
+ wdc_get_max_transfer_len(fd, &maximumTransferLength);
+
+ do
+ {
+ /* Get dumptrace size */
+ ret = wdc_de_VU_read_size(fd, 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(fd, 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(int fd, 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_log_page 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));
+ memset(&vuLogInput, 0, sizeof(vuLogInput));
+
+ if (wdc_get_serial_and_fw_rev(fd, (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(fd, &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(fd, 1, 0, &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_error_log(fd, elogNumEntries, 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_smart_log(fd, NVME_NSID_ALL, &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_log_page));
+ ret = nvme_fw_log(fd, &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_log_page));
+ }
+
+ /* 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(fd, WDC_DE_GLOBAL_NSID, deVULogPagesList[vuLogIdx].logPageId,
+ false, 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_feature(fd, WDC_DE_GLOBAL_NSID, deFeatureIdList[listIdx].featureId, FS_CURRENT, 0,
+ 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(fd, &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(fd, &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(fd, 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(fd, (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");
+ 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};
+ char *d_ptr;
+ int fd;
+ __u64 capabilities = 0;
+
+ struct config {
+ char *dirName;
+ };
+
+ struct config cfg = {
+ .dirName = NULL,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_STRING("dir-name", 'd', "DIRECTORY", &cfg.dirName, dirName),
+ OPT_END()
+ };
+
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_DRIVE_ESSENTIALS) != WDC_DRIVE_CAP_DRIVE_ESSENTIALS) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ return -1;
+ }
+
+ if (cfg.dirName != NULL) {
+ strncpy(d, cfg.dirName, PATH_MAX - 1);
+ d_ptr = d;
+ } else {
+ d_ptr = NULL;
+ }
+
+ return wdc_do_drive_essentials(fd, d_ptr, k);
+}
+
+static int wdc_do_drive_resize(int fd, uint64_t new_size)
+{
+ int ret;
+ struct nvme_admin_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_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(fd, &admin_cmd);
+ return ret;
+}
+
+static int wdc_do_namespace_resize(int fd, __u32 nsid, __u32 op_option)
+{
+ int ret;
+ struct nvme_admin_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_cmd));
+ admin_cmd.opcode = WDC_NVME_NAMESPACE_RESIZE_OPCODE;
+ admin_cmd.nsid = nsid;
+ admin_cmd.cdw10 = op_option;
+
+ ret = nvme_submit_admin_passthru(fd, &admin_cmd);
+ return ret;
+}
+
+static int wdc_do_drive_info(int fd, __u32 *result)
+{
+ int ret;
+ struct nvme_admin_cmd admin_cmd;
+
+ memset(&admin_cmd, 0, sizeof (struct nvme_admin_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(fd, &admin_cmd);
+
+ 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;
+ int fd, ret;
+
+ struct config {
+ uint64_t size;
+ };
+
+ struct config cfg = {
+ .size = 0,
+ };
+
+ OPT_ARGS(opts) = {
+ OPT_UINT("size", 's', &cfg.size, size),
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ wdc_check_device(fd);
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_RESIZE) == WDC_DRIVE_CAP_RESIZE) {
+ ret = wdc_do_drive_resize(fd, 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);
+
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ 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;
+ int fd, 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ 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");
+ return -1;
+ }
+
+ wdc_check_device(fd);
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_NS_RESIZE) == WDC_DRIVE_CAP_NS_RESIZE) {
+ ret = wdc_do_namespace_resize(fd, 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;
+ }
+
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ 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";
+ int fd;
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+
+ if (fd < 0)
+ return fd;
+
+ if (cfg.log_id != NVME_LOG_TELEMETRY_HOST && cfg.log_id != NVME_LOG_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_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(fd, f, PATH_MAX, fileSuffix) == -1) {
+ fprintf(stderr, "ERROR : WDC: failed to generate file name\n");
+ ret = -1;
+ goto close_fd;
+ }
+
+ snprintf(f + strlen(f), PATH_MAX, "%s", ".bin");
+ }
+
+ fprintf(stderr, "%s: filename = %s\n", __func__, f);
+
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_REASON_ID) == WDC_DRIVE_CAP_REASON_ID) {
+ ret = wdc_do_get_reason_id(fd, f, cfg.log_id);
+ } else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ }
+
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+
+ close_fd:
+ close(fd);
+ return ret;
+}
+
+static const char *nvme_log_id_to_string(__u8 log_id)
+{
+ switch (log_id) {
+ case NVME_LOG_ERROR: return "Error Information Log ID";
+ case NVME_LOG_SMART: return "Smart/Health Information Log ID";
+ case NVME_LOG_FW_SLOT: return "Firmware Slot Information Log ID";
+ case NVME_LOG_CHANGED_NS: return "Namespace Changed Log ID";
+ case NVME_LOG_CMD_EFFECTS: return "Commamds Supported and Effects Log ID";
+ case NVME_LOG_DEVICE_SELF_TEST: return "Device Self Test Log ID";
+ case NVME_LOG_TELEMETRY_HOST: return "Telemetry Host Initiated Log ID";
+ case NVME_LOG_TELEMETRY_CTRL: return "Telemetry Controller Generated Log ID";
+ case NVME_LOG_ENDURANCE_GROUP: return "Endurance Group Log ID";
+ case NVME_LOG_ANA: return "ANA Log ID";
+ case NVME_LOG_PERSISTENT_EVENT: return "Persistent Event Log ID";
+ case NVME_LOG_DISC: return "Discovery Log ID";
+ case NVME_LOG_RESERVATION: return "Reservation Notification Log ID";
+ case NVME_LOG_SANITIZE: return "Sanitize Status Log ID";
+
+ case WDC_LOG_ID_C0: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_C2: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_C4: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_C5: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_C6: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_CA: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_CB: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_D0: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_D6: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_D7: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_D8: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_DE: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_F0: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_F1: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_F2: return "WDC Vendor Unique Log ID";
+ case WDC_LOG_ID_FA: return "WDC Vendor Unique Log ID";
+
+ 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.";
+ int fd;
+ int ret = 0;
+ __u64 capabilities = 0;
+ struct wdc_c2_cbs_data *cbs_data = NULL;
+ int i;
+
+ 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ ret = validate_output_format(cfg.output_format);
+ if (ret < 0) {
+ fprintf(stderr, "%s: ERROR : WDC : invalid output format\n", __func__);
+ return ret;
+ }
+
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ if ((capabilities & WDC_DRIVE_CAP_LOG_PAGE_DIR) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ /* verify the 0xC2 Device Manageability log page is supported */
+ if (wdc_nvme_check_supported_log_page(fd, WDC_NVME_GET_DEV_MGMNT_LOG_PAGE_OPCODE) == false) {
+ fprintf(stderr, "%s: ERROR : WDC : 0xC2 Log Page not supported\n", __func__);
+ ret = -1;
+ goto out;
+ }
+
+ if (get_dev_mgment_cbs_data(fd, 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);
+ } 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:
+ return ret;
+}
+
+static int wdc_get_drive_reason_id(int fd, 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(fd, &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(int fd, __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(fd, 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) {
+ mkdir(reason_id_path, 0700);
+ }
+
+ 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(int fd)
+{
+ int ret = -1;
+ int verify_file;
+ char *reason_id_file;
+ char drive_reason_id[PATH_MAX] = {0};
+
+ if (wdc_get_drive_reason_id(fd, 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_do_get_reason_id(int fd, char *file, int log_id)
+{
+ int ret;
+ struct nvme_telemetry_log_page_hdr *log_hdr;
+ __u32 log_hdr_size = sizeof(struct nvme_telemetry_log_page_hdr);
+ __u32 reason_id_size = 0;
+
+ log_hdr = (struct nvme_telemetry_log_page_hdr *) 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(fd, 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_TELEMETRY_CTRL)
+ wdc_save_reason_id(fd, 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 int wdc_dump_telemetry_hdr(int fd, int log_id, struct nvme_telemetry_log_page_hdr *log_hdr)
+{
+ int ret = 0;
+ int host_gen = 0, ctrl_init = 0;
+
+ if (log_id == NVME_LOG_TELEMETRY_HOST)
+ host_gen = 1;
+ else
+ ctrl_init = 1;
+
+ ret = nvme_get_telemetry_log(fd, log_hdr, host_gen, ctrl_init, 512, 0);
+ 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 void wdc_print_nand_stats_normal(struct wdc_nand_stats *data)
+{
+ printf(" NAND Statistics :- \n");
+ printf(" NAND Writes TLC (Bytes) %.0Lf\n",
+ int128_to_double(data->nand_write_tlc));
+ printf(" NAND Writes SLC (Bytes) %.0Lf\n",
+ int128_to_double(data->nand_write_slc));
+ printf(" NAND Program Failures %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(data->nand_prog_failure));
+ printf(" NAND Erase Failures %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(data->nand_erase_failure));
+ printf(" Bad Block Count %"PRIu32"\n",
+ (uint32_t)le32_to_cpu(data->bad_block_count));
+ printf(" NAND XOR/RAID Recovery Trigger Events %"PRIu64"\n",
+ le64_to_cpu(data->nand_rec_trigger_event));
+ printf(" E2E Error Counter %"PRIu64"\n",
+ le64_to_cpu(data->e2e_error_counter));
+ printf(" Number Successful NS Resizing Events %"PRIu64"\n",
+ le64_to_cpu(data->successful_ns_resize_event));
+}
+
+static void wdc_print_nand_stats_json(struct wdc_nand_stats *data)
+{
+ struct json_object *root;
+
+ root = json_create_object();
+ json_object_add_value_float(root, "NAND Writes TLC (Bytes)",
+ int128_to_double(data->nand_write_tlc));
+ json_object_add_value_float(root, "NAND Writes SLC (Bytes)",
+ int128_to_double(data->nand_write_slc));
+ json_object_add_value_uint(root, "NAND Program Failures",
+ le32_to_cpu(data->nand_prog_failure));
+ json_object_add_value_uint(root, "NAND Erase Failures",
+ le32_to_cpu(data->nand_erase_failure));
+ json_object_add_value_uint(root, "Bad Block Count",
+ le32_to_cpu(data->bad_block_count));
+ json_object_add_value_uint(root, "NAND XOR/RAID Recovery Trigger Events",
+ le64_to_cpu(data->nand_rec_trigger_event));
+ json_object_add_value_uint(root, "E2E Error Counter",
+ le64_to_cpu(data->e2e_error_counter));
+ json_object_add_value_uint(root, "Number Successful NS Resizing Events",
+ le64_to_cpu(data->successful_ns_resize_event));
+
+ json_print_object(root, NULL);
+ printf("\n");
+ json_free_object(root);
+}
+
+static int wdc_do_vs_nand_stats(int fd, char *format)
+{
+ int ret;
+ int fmt = -1;
+ uint8_t *output = NULL;
+ struct wdc_nand_stats *nand_stats;
+
+ 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(fd, 0xFFFFFFFF, WDC_NVME_NAND_STATS_LOG_ID,
+ false, 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;
+ }
+
+ /* parse the data */
+ nand_stats = (struct wdc_nand_stats *)(output);
+ switch (fmt) {
+ case NORMAL:
+ wdc_print_nand_stats_normal(nand_stats);
+ break;
+ case JSON:
+ wdc_print_nand_stats_json(nand_stats);
+ 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.";
+ int fd;
+ int ret = 0;
+ __u64 capabilities = 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()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ capabilities = wdc_get_drive_capabilities(fd);
+
+ if ((capabilities & WDC_DRIVE_CAP_NAND_STATS) == 0) {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ } else {
+ ret = wdc_do_vs_nand_stats(fd, cfg.output_format);
+ if (ret)
+ fprintf(stderr, "ERROR : WDC : Failure reading NAND statistics, ret = %d\n", ret);
+ }
+
+ 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.";
+ uint64_t capabilities = 0;
+ int fd, ret;
+ __le32 result;
+ __u16 size;
+ double rev;
+
+ OPT_ARGS(opts) = {
+ OPT_END()
+ };
+
+ fd = parse_and_open(argc, argv, desc, opts);
+ if (fd < 0)
+ return fd;
+
+ wdc_check_device(fd);
+ capabilities = wdc_get_drive_capabilities(fd);
+ if ((capabilities & WDC_DRIVE_CAP_INFO) == WDC_DRIVE_CAP_INFO) {
+ ret = wdc_do_drive_info(fd, &result);
+ } else {
+ fprintf(stderr, "ERROR : WDC: unsupported device for this command\n");
+ ret = -1;
+ }
+
+ if (!ret) {
+ size = (__u16)((cpu_to_le32(result) & 0xffff0000) >> 16);
+ rev = (double)(cpu_to_le32(result) & 0x0000ffff);
+
+ printf("Drive HW Revison: %4.1f\n", (.1 * rev));
+ printf("FTL Unit Size: 0x%x KB\n", size);
+ }
+
+ fprintf(stderr, "NVMe Status:%s(%x)\n", nvme_status_to_string(ret), ret);
+ return ret;
+}
+
+
diff --git a/plugins/wdc/wdc-nvme.h b/plugins/wdc/wdc-nvme.h
new file mode 100644
index 0000000..c6b2d3f
--- /dev/null
+++ b/plugins/wdc/wdc-nvme.h
@@ -0,0 +1,38 @@
+#undef CMD_INC_FILE
+#define CMD_INC_FILE plugins/wdc/wdc-nvme
+
+#if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ)
+#define WDC_NVME
+
+#include "cmd.h"
+
+PLUGIN(NAME("wdc", "Western Digital vendor specific extensions"),
+ 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("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)
+ )
+);
+
+#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..80d9955
--- /dev/null
+++ b/plugins/wdc/wdc-utils.c
@@ -0,0 +1,146 @@
+/*
+ * 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;
+ timeInfo->zone = -currTimeInfo.tm_gmtoff / 60;
+
+ 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;
+}
+
diff --git a/plugins/wdc/wdc-utils.h b/plugins/wdc/wdc-utils.h
new file mode 100644
index 0000000..251339f
--- /dev/null
+++ b/plugins/wdc/wdc-utils.h
@@ -0,0 +1,77 @@
+/*
+ * 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);
+