From: Quyen Truong Date: Tue, 21 May 2019 11:48:37 +0000 (+0700) Subject: Add Virtium plugin extension X-Git-Tag: v1.9~55^2~1 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=fb7f63bd74f2adb5ac3557e7cc3e19ce1494f430;p=users%2Fsagi%2Fnvme-cli.git Add Virtium plugin extension --- diff --git a/Makefile b/Makefile index 4bfbebbd..ebb6b75b 100644 --- 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 index 00000000..a67f5d38 --- /dev/null +++ b/plugins/virtium/virtium-nvme.c @@ -0,0 +1,953 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(¤t); + + tstamp = *localtime(¤t); + 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 index 00000000..b95c910c --- /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"