]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
Add Virtium plugin extension
authorQuyen Truong <quyen.truong@virtium.com>
Tue, 21 May 2019 11:48:37 +0000 (18:48 +0700)
committerQuyen Truong <quyen.truong@virtium.com>
Tue, 21 May 2019 11:48:37 +0000 (18:48 +0700)
Makefile
plugins/virtium/virtium-nvme.c [new file with mode: 0644]
plugins/virtium/virtium-nvme.h [new file with mode: 0644]

index 4bfbebbd156a6f2758345a7a7c934cfb6d1627dd..ebb6b75b4a12ec06d1b130ae13817d5b74864152 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,8 @@ PLUGIN_OBJS :=                                        \
        plugins/netapp/netapp-nvme.o            \
        plugins/toshiba/toshiba-nvme.o          \
        plugins/micron/micron-nvme.o            \
-       plugins/seagate/seagate-nvme.o
+       plugins/seagate/seagate-nvme.o          \
+       plugins/virtium/virtium-nvme.o
 
 nvme: nvme.c nvme.h $(OBJS) $(PLUGIN_OBJS) NVME-VERSION-FILE
        $(CC) $(CPPFLAGS) $(CFLAGS) nvme.c -o $(NVME) $(OBJS) $(PLUGIN_OBJS) $(LDFLAGS)
diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c
new file mode 100644 (file)
index 0000000..a67f5d3
--- /dev/null
@@ -0,0 +1,953 @@
+#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     16384
+#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;
+
+    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;%f;", (double)smart->raw_ns.nsze / 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), "Reversed_1;%d;\n", 0);
+    strcat(text, tempbuff);
+}
+
+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] = "";
+
+    // fill session
+    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);
+
+    // fill device
+    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(0);
+    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(0);
+
+    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);
+}
+
+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_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[18] = {"Reversed", \
+                                 "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 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, 2, 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("    \"Optional NVM Command 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("    \"VNVM 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");
+}
+
+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 = 0.25,
+        .output_file = NULL,
+        .test_name = NULL,
+    };
+       
+    const struct argconfig_commandline_options command_line_options[] = \
+    {
+        {"run-time",    'r', "NUM", CFG_DOUBLE, &cfg.run_time_hrs, required_argument, run_time},
+        {"freq",        'f', "NUM", CFG_DOUBLE, &cfg.log_record_frequency_hrs, required_argument, freq},
+        {"output-file", 'o', "FILE", CFG_STRING, &cfg.output_file, required_argument, output_file},
+        {"test-name",   'n', "NAME", CFG_STRING, &cfg.test_name, required_argument, test_name},
+        {NULL}
+    };
+
+    vt_generate_vtview_log_file_name(vt_default_log_file_name);
+
+    fd = parse_and_open(argc, argv, desc, command_line_options, &cfg, sizeof(cfg));
+    if (fd < 0) 
+    {
+        printf("Error parse and open (fd = %d)\n", fd);
+               
+        return (fd);
+    }
+
+    printf("argc: %d\n", argc);
+    strcpy(path, argv[1]);
+
+    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);
+    }
+
+    // start get log
+    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(0);
+    end_time = start_time + total_time;
+
+    fflush(stdout);
+       
+    while(1)
+    {
+        cur_time = time(0);
+        if(cur_time >= end_time)
+        {
+            break;
+        }
+
+        // update log
+        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";
+
+    const struct argconfig_commandline_options command_line_options[] = \
+    {
+         {NULL}
+    };
+
+    fd = parse_and_open(argc, argv, desc, command_line_options, NULL, 0);
+    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));
+    // print out detail identify
+    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 (file)
index 0000000..b95c910
--- /dev/null
@@ -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"