diff options
Diffstat (limited to '')
-rw-r--r-- | plugins/virtium/virtium-nvme.c | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/plugins/virtium/virtium-nvme.c b/plugins/virtium/virtium-nvme.c new file mode 100644 index 0000000..0ba4b15 --- /dev/null +++ b/plugins/virtium/virtium-nvme.c @@ -0,0 +1,1051 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <stddef.h> +#include <inttypes.h> +#include <stdbool.h> +#include <time.h> +#include <locale.h> + +#include "common.h" +#include "nvme.h" +#include "libnvme.h" +#include "plugin.h" +#include "util/types.h" + +#define CREATE_CMD +#include "virtium-nvme.h" + +#define MIN2(a, b) (((a) < (b)) ? (a) : (b)) + +#define HOUR_IN_SECONDS 3600 + +#define MAX_HEADER_BUFF (20 * 1024) +#define MAX_LOG_BUFF 4096 +#define DEFAULT_TEST_NAME "Put the name of your test here" + +static char vt_default_log_file_name[256]; + +struct vtview_log_header { + char path[256]; + char test_name[256]; + long time_stamp; + struct nvme_id_ctrl raw_ctrl; + struct nvme_firmware_slot raw_fw; +}; + +struct vtview_smart_log_entry { + char path[256]; + long time_stamp; + struct nvme_id_ns raw_ns; + struct nvme_id_ctrl raw_ctrl; + struct nvme_smart_log raw_smart; +}; + +struct vtview_save_log_settings { + double run_time_hrs; + double log_record_frequency_hrs; + const char *output_file; + const char *test_name; +}; + +static void vt_initialize_header_buffer(struct vtview_log_header *pbuff) +{ + memset(pbuff->path, 0, sizeof(pbuff->path)); + memset(pbuff->test_name, 0, sizeof(pbuff->test_name)); +} + +static void vt_convert_data_buffer_to_hex_string(const unsigned char *bufPtr, + const unsigned int size, const bool isReverted, char *output) +{ + unsigned int i, pos; + const char hextable[16] = { + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', 'A', 'B', + 'C', 'D', 'E', 'F', + }; + + memset(output, 0, (size * 2) + 1); + + for (i = 0; i < size; i++) { + if (isReverted) + pos = size - 1 - i; + else + pos = i; + output[2 * i] = hextable[(bufPtr[pos] & 0xF0) >> 4]; + output[2 * i + 1] = hextable[(bufPtr[pos] & 0x0F)]; + } +} + +/* + * Generate log file name. + * Log file name will be generated automatically if user leave log file option blank. + * Log file name will be generated as vtView-Smart-log-date-time.txt + */ +static void vt_generate_vtview_log_file_name(char *fname) +{ + time_t current; + struct tm tstamp; + char temp[256]; + + time(¤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; + double capacity; + char *curlocale; + char *templocale; + __u8 lba_index; + + nvme_id_ns_flbas_to_lbaf_inuse(smart->raw_ns.flbas, &lba_index); + + curlocale = setlocale(LC_ALL, NULL); + templocale = strdup(curlocale); + + if (!templocale) + printf("Cannot malloc buffer\n"); + + setlocale(LC_ALL, "C"); + + unsigned long long lba = 1ULL << smart->raw_ns.lbaf[lba_index].ds; + + capacity = le64_to_cpu(smart->raw_ns.nsze) * lba; + + snprintf(tempbuff, sizeof(tempbuff), "log;%s;%lu;%s;%s;%-.*s;", smart->raw_ctrl.sn, smart->time_stamp, smart->path, + smart->raw_ctrl.mn, (int)sizeof(smart->raw_ctrl.fr), smart->raw_ctrl.fr); + strcpy(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Capacity;%lf;", capacity / 1000000000); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Critical_Warning;%u;", smart->raw_smart.critical_warning); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Temperature;%u;", temperature); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Available_Spare;%u;", smart->raw_smart.avail_spare); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Available_Spare_Threshold;%u;", smart->raw_smart.spare_thresh); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Percentage_Used;%u;", smart->raw_smart.percent_used); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Data_Units_Read;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.data_units_read))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Data_Units_Written;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.data_units_written))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Host_Read_Commands;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.host_reads))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Host_Write_Commands;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.host_writes))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Controller_Busy_Time;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.ctrl_busy_time))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Power_Cycles;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.power_cycles))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Power_On_Hours;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.power_on_hours))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Unsafe_Shutdowns;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.unsafe_shutdowns))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Media_Errors;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.media_errors))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Num_Err_Log_Entries;%s;", uint128_t_to_string(le128_to_cpu(smart->raw_smart.num_err_log_entries))); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Warning_Temperature_Time;%u;", le32_to_cpu(smart->raw_smart.warning_temp_time)); + strcat(text, tempbuff); + snprintf(tempbuff, sizeof(tempbuff), "Critical_Composite_Temperature_Time;%u;", le32_to_cpu(smart->raw_smart.critical_comp_time)); + strcat(text, tempbuff); + + for (i = 0; i < 8; i++) { + __s32 temp = le16_to_cpu(smart->raw_smart.temp_sensor[i]); + + if (!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 (!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) + return; + + i = size - 1; + while (i && (' ' == str[i])) { + str[i] = 0; + i--; + } +} + +static int vt_add_entry_to_log(const int fd, const char *path, const struct vtview_save_log_settings *cfg) +{ + struct vtview_smart_log_entry smart; + const char *filename; + int ret = 0; + unsigned int nsid = 0; + + memset(smart.path, 0, sizeof(smart.path)); + strncpy(smart.path, path, sizeof(smart.path) - 1); + if (!cfg->output_file) + filename = vt_default_log_file_name; + else + filename = cfg->output_file; + + smart.time_stamp = time(NULL); + ret = nvme_get_nsid(fd, &nsid); + + if (ret < 0) { + printf("Cannot read namespace-id\n"); + return -1; + } + + ret = nvme_identify_ns(fd, nsid, &smart.raw_ns); + if (ret) { + printf("Cannot read namespace identify\n"); + return -1; + } + + ret = nvme_identify_ctrl(fd, &smart.raw_ctrl); + if (ret) { + printf("Cannot read device identify controller\n"); + return -1; + } + + ret = nvme_get_log_smart(fd, NVME_NSID_ALL, false, &smart.raw_smart); + if (ret) { + printf("Cannot read device SMART log\n"); + return -1; + } + + vt_process_string(smart.raw_ctrl.sn, sizeof(smart.raw_ctrl.sn)); + vt_process_string(smart.raw_ctrl.mn, sizeof(smart.raw_ctrl.mn)); + + ret = vt_append_log(&smart, filename); + return ret; +} + +static int vt_update_vtview_log_header(const int fd, const char *path, const struct vtview_save_log_settings *cfg) +{ + struct vtview_log_header header; + const char *filename; + int ret = 0; + + vt_initialize_header_buffer(&header); + if (strlen(path) > sizeof(header.path)) { + printf("filename too long\n"); + errno = EINVAL; + return -1; + } + strcpy(header.path, path); + + if (!cfg->test_name) { + strcpy(header.test_name, DEFAULT_TEST_NAME); + } else { + if (strlen(cfg->test_name) > sizeof(header.test_name)) { + printf("test name too long\n"); + errno = EINVAL; + return -1; + } + strcpy(header.test_name, cfg->test_name); + } + + if (!cfg->output_file) + filename = vt_default_log_file_name; + else + filename = cfg->output_file; + + printf("Log file: %s\n", filename); + header.time_stamp = time(NULL); + + ret = nvme_identify_ctrl(fd, &header.raw_ctrl); + if (ret) { + printf("Cannot read identify device\n"); + return -1; + } + + ret = nvme_get_log_fw_slot(fd, false, &header.raw_fw); + if (ret) { + printf("Cannot read device firmware log\n"); + return -1; + } + + vt_process_string(header.raw_ctrl.sn, sizeof(header.raw_ctrl.sn)); + vt_process_string(header.raw_ctrl.mn, sizeof(header.raw_ctrl.mn)); + + ret = vt_append_header(&header, filename); + return ret; +} + +static void vt_build_identify_lv2(unsigned int data, unsigned int start, + unsigned int count, const char **table, + bool isEnd) +{ + unsigned int i, end, pos, sh = 1; + unsigned int temp; + + end = start + count; + + for (i = start; i < end; i++) { + temp = ((data & (sh << i)) >> i); + pos = i * 2; + printf(" \"bit %u\":\"%ub %s\"\n", i, temp, table[pos]); + printf(" %s", table[pos + 1]); + + if ((end - 1) != i || !isEnd) + printf(",\n"); + else + printf("\n"); + } + + if (isEnd) + printf(" },\n"); +} + +static void vt_build_power_state_descriptor(const struct nvme_id_ctrl *ctrl) +{ + unsigned int i; + unsigned char *buf; + + printf("{\n"); + printf("\"Power State Descriptors\":{\n"); + printf(" \"NOPS\":\"Non-Operational State,\"\n"); + printf(" \"MPS\":\"Max Power Scale (0: in 0.01 Watts; 1: in 0.0001 Watts),\"\n"); + printf(" \"ENLAT\":\"Entry Latency in microseconds,\"\n"); + printf(" \"RWL\":\"Relative Write Latency,\"\n"); + printf(" \"RRL\":\"Relative Read Latency,\"\n"); + printf(" \"IPS\":\"Idle Power Scale (00b: Not reported; 01b: 0.0001 W; 10b: 0.01 W; 11b: Reserved),\"\n"); + printf(" \"APS\":\"Active Power Scale (00b: Not reported; 01b: 0.0001 W; 10b: 0.01 W; 11b: Reserved),\"\n"); + printf(" \"ACTP\":\"Active Power,\"\n"); + printf(" \"MP\":\"Maximum Power,\"\n"); + printf(" \"EXLAT\":\"Exit Latency in microsecond,\"\n"); + printf(" \"RWT\":\"Relative Write Throughput,\"\n"); + printf(" \"RRT\":\"Relative Read Throughput,\"\n"); + printf(" \"IDLP\":\"Idle Power,\"\n"); + printf(" \"APW\":\"Active Power Workload,\"\n"); + printf(" \"Ofs\":\"BYTE Offset,\"\n"); + + printf(" \"Power State Descriptors\":\"\n"); + + printf("%6s%10s%5s%4s%6s%10s%10s%10s%4s%4s%4s%4s%10s%4s%6s%10s%4s%5s%6s\n", "Entry", "0fs 00-03", "NOPS", "MPS", "MP", "ENLAT", "EXLAT", "0fs 12-15", + "RWL", "RWT", "RRL", "RRT", "0fs 16-19", "IPS", "IDLP", "0fs 20-23", "APS", "APW", "ACTP"); + + + printf("%6s%10s%5s%4s%6s%10s%10s%10s%4s%4s%4s%4s%10s%4s%6s%10s%4s%5s%6s\n", "=====", "=========", "====", "===", "=====", "=========", "=========", + "=========", "===", "===", "===", "===", "=========", "===", "=====", "=========", "===", "====", "====="); + + for (i = 0; i < 32; i++) { + char s[100]; + unsigned int temp; + + printf("%6d", i); + buf = (unsigned char *) (&ctrl->psd[i]); + vt_convert_data_buffer_to_hex_string(&buf[0], 4, true, s); + printf("%9sh", s); + + temp = ctrl->psd[i].flags; + printf("%4ub", ((unsigned char)temp & 0x02)); + printf("%3ub", ((unsigned char)temp & 0x01)); + vt_convert_data_buffer_to_hex_string(&buf[0], 2, true, s); + printf("%5sh", s); + + vt_convert_data_buffer_to_hex_string(&buf[4], 4, true, s); + printf("%9sh", s); + vt_convert_data_buffer_to_hex_string(&buf[8], 4, true, s); + printf("%9sh", s); + vt_convert_data_buffer_to_hex_string(&buf[12], 4, true, s); + printf("%9sh", s); + vt_convert_data_buffer_to_hex_string(&buf[15], 1, true, s); + printf("%3sh", s); + vt_convert_data_buffer_to_hex_string(&buf[14], 1, true, s); + printf("%3sh", s); + vt_convert_data_buffer_to_hex_string(&buf[13], 1, true, s); + printf("%3sh", s); + vt_convert_data_buffer_to_hex_string(&buf[12], 1, true, s); + printf("%3sh", s); + vt_convert_data_buffer_to_hex_string(&buf[16], 4, true, s); + printf("%9sh", s); + + temp = ctrl->psd[i].ips; + snprintf(s, sizeof(s), "%u%u", (((unsigned char)temp >> 6) & 0x01), (((unsigned char)temp >> 7) & 0x01)); + printf("%3sb", s); + + vt_convert_data_buffer_to_hex_string(&buf[16], 2, true, s); + printf("%5sh", s); + vt_convert_data_buffer_to_hex_string(&buf[20], 4, true, s); + printf("%9sh", s); + + temp = ctrl->psd[i].apws; + snprintf(s, sizeof(s), "%u%u", (((unsigned char)temp >> 6) & 0x01), (((unsigned char)temp >> 7) & 0x01)); + printf("%3sb", s); + snprintf(s, sizeof(s), "%u%u%u", (((unsigned char)temp) & 0x01), (((unsigned char)temp >> 1) & 0x01), (((unsigned char)temp >> 2) & 0x01)); + printf("%4sb", s); + + vt_convert_data_buffer_to_hex_string(&buf[20], 2, true, s); + printf("%5sh", s); + printf("\n"); + } + + printf(" \"}\n}\n"); + +} + +static void vt_dump_hex_data(const unsigned char *pbuff, size_t pbuffsize) +{ + char textbuf[33]; + unsigned long 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)) || ((i + 1) == pbuffsize)) { + printf(" "); + if (!((i + 1) % 32)) { + 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)) + printf(" "); + + for (j = ((i + 1) % 32); j < 32; j++) { + printf(" "); + if (!((j + 1) % 8)) + printf(" "); + } + + printf("%s\n", textbuf); + } + } + } +} + +static void vt_parse_detail_identify(const struct nvme_id_ctrl *ctrl) +{ + unsigned char *buf; + unsigned int temp, pos; + char s[1024] = ""; + + const char *CMICtable[6] = {"0 = the NVM subsystem contains only a single NVM subsystem port", + "1 = the NVM subsystem may contain more than one subsystem ports", + "0 = the NVM subsystem contains only a single controller", + "1 = the NVM subsystem may contain two or more controllers (see section 1.4.1)", + "0 = the controller is associated with a PCI Function or a Fabrics connection", + "1 = the controller is associated with an SR-IOV Virtual Function"}; + + const char *OAEStable[20] = {"Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "0 = does not support sending the Namespace Attribute Notices event nor the associated Changed Namespace List log page", + "1 = supports sending the Namespace Attribute Notices & the associated Changed Namespace List log page", + "0 = does not support sending Firmware Activation Notices event", + "1 = supports sending Firmware Activation Notices"}; + + const char *CTRATTtable[4] = {"0 = does not support a 128-bit Host Identifier", + "1 = supports a 128-bit Host Identifier", + "0 = does not support Non-Operational Power State Permissive Mode", + "1 = supports Non-Operational Power State Permissive Mode"}; + + const char *OACStable[18] = {"0 = does not support the Security Send and Security Receive commands", + "1 = supports the Security Send and Security Receive commands", + "0 = does not support the Format NVM command", + "1 = supports the Format NVM command", + "0 = does not support the Firmware Commit and Firmware Image Download commands", + "1 = supports the Firmware Commit and Firmware Image Download commands", + "0 = does not support the Namespace Management capability", + "1 = supports the Namespace Management capability", + "0 = does not support the Device Self-test command", + "1 = supports the Device Self-test command", + "0 = does not support Directives", + "1 = supports Directive Send & Directive Receive commands", + "0 = does not support the NVMe-MI Send and NVMe-MI Receive commands", + "1 = supports the NVMe-MI Send and NVMe-MI Receive commands", + "0 = does not support the Virtualization Management command", + "1 = supports the Virtualization Management command", + "0 = does not support the Doorbell Buffer Config command", + "1 = supports the Doorbell Buffer Config command"}; + + const char *FRMWtable[10] = {"0 = the 1st firmware slot (slot 1) is read/write", + "1 = the 1st firmware slot (slot 1) is read only", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "0 = requires a reset for firmware to be activated", + "1 = supports firmware activation without a reset"}; + + const char *LPAtable[8] = {"0 = does not support the SMART / Health information log page on a per namespace basis", + "1 = supports the SMART / Health information log page on a per namespace basis", + "0 = does not support the Commands Supported & Effects log page", + "1 = supports the Commands Supported Effects log page", + "0 = does not support extended data for Get Log Page", + "1 = supports extended data for Get Log Page (including extended Number of Dwords and Log Page Offset fields)", + "0 = does not support the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and Telemetry Log Notices events", + "1 = supports the Telemetry Host-Initiated and Telemetry Controller-Initiated log pages and sending Telemetry Log Notices"}; + + const char *AVSCCtable[2] = {"0 = the format of all Admin Vendor Specific Commands are vendor specific", + "1 = all Admin Vendor Specific Commands use the format defined in NVM Express specification"}; + + const char *APSTAtable[2] = {"0 = does not support autonomous power state transitions", + "1 = supports autonomous power state transitions"}; + + const char *DSTOtable[2] = {"0 = the NVM subsystem supports one device self-test operation per controller at a time", + "1 = the NVM subsystem supports only one device self-test operation in progress at a time"}; + + const char *HCTMAtable[2] = {"0 = does not support host controlled thermal management", + "1 = supports host controlled thermal management. Supports Set Features & Get Features commands with the Feature Identifier field set to 10h"}; + + const char *SANICAPtable[6] = {"0 = does not support the Crypto Erase sanitize operation", + "1 = supports the Crypto Erase sanitize operation", + "0 = does not support the Block Erase sanitize operation", + "1 = supports the Block Erase sanitize operation", + "0 = does not support the Overwrite sanitize operation", + "1 = supports the Overwrite sanitize operation"}; + + const char *ONCStable[14] = {"0 = does not support the Compare command", + "1 = supports the Compare command", + "0 = does not support the Write Uncorrectable command", + "1 = supports the Write Uncorrectable command", + "0 = does not support the Dataset Management command", + "1 = supports the Dataset Management command", + "0 = does not support the Write Zeroes command", + "1 = supports the Write Zeroes command", + "0 = does not support the Save field set to a non-zero value in the Set Features and the Get Features commands", + "1 = supports the Save field set to a non-zero value in the Set Features and the Get Features commands", + "0 = does not support reservations", + "1 = supports reservations", + "0 = does not support the Timestamp feature (refer to section 5.21.1.14)", + "1 = supports the Timestamp feature"}; + + const char *FUSEStable[2] = {"0 = does not support the Compare and Write fused operation", + "1 = supports the Compare and Write fused operation"}; + + const char *FNAtable[6] = {"0 = supports format on a per namespace basis", + "1 = all namespaces shall be configured with the same attributes and a format (excluding secure erase) of any namespace results in a format of all namespaces in an NVM subsystem", + "0 = any secure erase performed as part of a format results in a secure erase of a particular namespace specified", + "1 = any secure erase performed as part of a format operation results in a secure erase of all namespaces in the NVM subsystem", + "0 = cryptographic erase is not supported", + "1 = cryptographic erase is supported as part of the secure erase functionality"}; + + const char *VWCtable[2] = {"0 = a volatile write cache is not present", + "1 = a volatile write cache is present"}; + + const char *ICSVSCCtable[2] = {"0 = the format of all NVM Vendor Specific Commands are vendor specific", + "1 = all NVM Vendor Specific Commands use the format defined in NVM Express specification"}; + + const char *SGLSSubtable[4] = {"00b = SGLs are not supported", + "01b = SGLs are supported. There is no alignment nor granularity requirement for Data Blocks", + "10b = SGLs are supported. There is a Dword alignment and granularity requirement for Data Blocks", + "11b = Reserved"}; + + const char *SGLStable[42] = {"Used", + "Used", + "Used", + "Used", + "0 = does not support the Keyed SGL Data Block descriptor", + "1 = supports the Keyed SGL Data Block descriptor", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "0 = the SGL Bit Bucket descriptor is not supported", + "1 = the SGL Bit Bucket descriptor is supported", + "0 = use of a byte aligned contiguous physical buffer of metadata is not supported", + "1 = use of a byte aligned contiguous physical buffer of metadata is supported", + "0 = the SGL length shall be equal to the amount of data to be transferred", + "1 = supports commands that contain a data or metadata SGL of a length larger than the amount of data to be transferred", + "0 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is not supported", + "1 = use of Metadata Pointer (MPTR) that contains an address of an SGL segment containing exactly one SGL Descriptor that is Qword aligned is supported", + "0 = the Address field specifying an offset is not supported", + "1 = supports the Address field in SGL Data Block, SGL Segment, and SGL Last Segment descriptor types specifying an offset"}; + + buf = (unsigned char *)(ctrl); + + printf("{\n"); + vt_convert_data_buffer_to_hex_string(buf, 2, true, s); + printf(" \"PCI Vendor ID\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[2], 2, true, s); + printf(" \"PCI Subsystem Vendor ID\":\"%sh\",\n", s); + printf(" \"Serial Number\":\"%s\",\n", ctrl->sn); + printf(" \"Model Number\":\"%s\",\n", ctrl->mn); + printf(" \"Firmware Revision\":\"%-.*s\",\n", (int)sizeof(ctrl->fr), ctrl->fr); + vt_convert_data_buffer_to_hex_string(&buf[72], 1, true, s); + printf(" \"Recommended Arbitration Burst\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[73], 3, true, s); + printf(" \"IEEE OUI Identifier\":\"%sh\",\n", s); + + temp = ctrl->cmic; + printf(" \"Controller Multi-Path I/O and Namespace Sharing Capabilities\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[76], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 3, CMICtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[77], 1, true, s); + printf(" \"Maximum Data Transfer Size\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[78], 2, true, s); + printf(" \"Controller ID\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[80], 4, true, s); + printf(" \"Version\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[84], 4, true, s); + printf(" \"RTD3 Resume Latency\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[88], 4, true, s); + printf(" \"RTD3 Entry Latency\":\"%sh\",\n", s); + + temp = le32_to_cpu(ctrl->oaes); + printf(" \"Optional Asynchronous Events Supported\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[92], 4, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 8, 2, OAEStable, true); + + temp = le32_to_cpu(ctrl->ctratt); + printf(" \"Controller Attributes\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[96], 4, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 2, CTRATTtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[122], 16, true, s); + printf(" \"FRU Globally Unique Identifier\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[240], 16, true, s); + printf(" \"NVMe Management Interface Specification\":\"%sh\",\n", s); + + temp = le16_to_cpu(ctrl->oacs); + printf(" \"Optional Admin Command Support\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[256], 2, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 9, OACStable, true); + + vt_convert_data_buffer_to_hex_string(&buf[258], 1, true, s); + printf(" \"Abort Command Limit\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[259], 1, true, s); + printf(" \"Asynchronous Event Request Limit\":\"%sh\",\n", s); + + temp = ctrl->frmw; + printf(" \"Firmware Updates\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[260], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, FRMWtable, false); + vt_convert_data_buffer_to_hex_string(&buf[260], 1, true, s); + printf(" \"Firmware Slot\":\"%uh\",\n", ((ctrl->frmw >> 1) & 0x07)); + vt_build_identify_lv2(temp, 4, 1, FRMWtable, true); + + temp = ctrl->lpa; + printf(" \"Log Page Attributes\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[261], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 4, LPAtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[262], 1, true, s); + printf(" \"Error Log Page Entries\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[263], 1, true, s); + printf(" \"Number of Power States Support\":\"%sh\",\n", s); + + temp = ctrl->avscc; + printf(" \"Admin Vendor Specific Command Configuration\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[264], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, AVSCCtable, true); + + temp = ctrl->apsta; + printf(" \"Autonomous Power State Transition Attributes\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[265], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, APSTAtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[266], 2, true, s); + printf(" \"Warning Composite Temperature Threshold\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[268], 2, true, s); + printf(" \"Critical Composite Temperature Threshold\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[270], 2, true, s); + printf(" \"Maximum Time for Firmware Activation\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[272], 4, true, s); + printf(" \"Host Memory Buffer Preferred Size\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[276], 4, true, s); + printf(" \"Host Memory Buffer Minimum Size\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[280], 16, true, s); + printf(" \"Total NVM Capacity\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[296], 16, true, s); + printf(" \"Unallocated NVM Capacity\":\"%sh\",\n", s); + + temp = le32_to_cpu(ctrl->rpmbs); + printf(" \"Replay Protected Memory Block Support\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[312], 4, true, s); + printf(" \"Value\":\"%sh\",\n", s); + printf(" \"Number of RPMB Units\":\"%u\",\n", (temp & 0x00000003)); + snprintf(s, sizeof(s), ((temp >> 3) & 0x00000007) ? "Reserved" : "HMAC SHA-256"); + printf(" \"Authentication Method\":\"%u: %s\",\n", ((temp >> 3) & 0x00000007), s); + printf(" \"Total Size\":\"%u\",\n", ((temp >> 16) & 0x000000FF)); + printf(" \"Access Size\":\"%u\",\n", ((temp >> 24) & 0x000000FF)); + printf(" },\n"); + + vt_convert_data_buffer_to_hex_string(&buf[316], 2, true, s); + printf(" \"Extended Device Self-test Time\":\"%sh\",\n", s); + + temp = ctrl->dsto; + printf(" \"Device Self-test Options\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[318], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, DSTOtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[319], 1, true, s); + printf(" \"Firmware Update Granularity\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[320], 1, true, s); + printf(" \"Keep Alive Support\":\"%sh\",\n", s); + + temp = le16_to_cpu(ctrl->hctma); + printf(" \"Host Controlled Thermal Management Attributes\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[322], 2, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, HCTMAtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[324], 2, true, s); + printf(" \"Minimum Thermal Management Temperature\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[326], 2, true, s); + printf(" \"Maximum Thermal Management Temperature\":\"%sh\",\n", s); + + temp = le16_to_cpu(ctrl->sanicap); + printf(" \"Sanitize Capabilities\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[328], 2, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 3, SANICAPtable, true); + + temp = ctrl->sqes; + printf(" \"Submission Queue Entry Size\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[512], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + printf(" \"Maximum Size\":\"%u\",\n", (temp & 0x0000000F)); + printf(" \"Required Size\":\"%u\",\n", ((temp >> 4) & 0x0000000F)); + printf(" }\n"); + + temp = ctrl->cqes; + printf(" \"Completion Queue Entry Size\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[513], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + printf(" \"Maximum Size\":\"%u\",\n", (temp & 0x0000000F)); + printf(" \"Required Size\":\"%u\",\n", ((temp >> 4) & 0x0000000F)); + printf(" }\n"); + + vt_convert_data_buffer_to_hex_string(&buf[514], 2, true, s); + printf(" \"Maximum Outstanding Commands\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[516], 4, true, s); + printf(" \"Number of Namespaces\":\"%sh\",\n", s); + + temp = le16_to_cpu(ctrl->oncs); + printf(" \"Optional NVM Command Support\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[520], 2, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 7, ONCStable, true); + + temp = le16_to_cpu(ctrl->fuses); + printf(" \"Fused Operation Support\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[522], 2, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, FUSEStable, true); + + temp = ctrl->fna; + printf(" \"Format NVM Attributes\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[524], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 3, FNAtable, true); + + temp = ctrl->vwc; + printf(" \"Volatile Write Cache\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[525], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, VWCtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[526], 2, true, s); + printf(" \"Atomic Write Unit Normal\":\"%sh\",\n", s); + vt_convert_data_buffer_to_hex_string(&buf[528], 2, true, s); + printf(" \"Atomic Write Unit Power Fail\":\"%sh\",\n", s); + + temp = ctrl->icsvscc; + printf(" \"NVM Vendor Specific Command Configuration\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[530], 1, true, s); + printf(" \"Value\":\"%sh\",\n", s); + vt_build_identify_lv2(temp, 0, 1, ICSVSCCtable, true); + + vt_convert_data_buffer_to_hex_string(&buf[532], 2, true, s); + printf(" \"Atomic Compare 0 Write Unit\":\"%sh\",\n", s); + + temp = le32_to_cpu(ctrl->sgls); + printf(" \"SGL Support\":{\n"); + vt_convert_data_buffer_to_hex_string(&buf[536], 4, true, s); + printf(" \"Value\":\"%sh\",\n", s); + pos = (temp & 0x00000003); + printf(" \"bit 1:0\":\"%s\",\n", SGLSSubtable[pos]); + vt_build_identify_lv2(temp, 2, 1, SGLStable, false); + vt_build_identify_lv2(temp, 16, 5, SGLStable, true); + + vt_convert_data_buffer_to_hex_string(&buf[768], 256, false, s); + printf(" \"NVM Subsystem NVMe Qualified Name\":\"%s\",\n", s); + printf("}\n\n"); + + vt_build_power_state_descriptor(ctrl); + + + printf("\n{\n"); + printf("\"Vendor Specific\":\"\n"); + vt_dump_hex_data(&buf[3072], 1024); + printf("\"}\n"); +} + +static int vt_save_smart_to_vtview_log(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int ret, err = 0; + long total_time = 0; + long freq_time = 0; + long cur_time = 0; + long remain_time = 0; + long start_time = 0; + long end_time = 0; + char path[256] = ""; + char *desc = "Save SMART data into log file with format that is easy to analyze (comma delimited). Maximum log file will be 4K.\n\n" + "Typical usages:\n\n" + "Temperature characterization:\n" + "\tvirtium save-smart-to-vtview-log /dev/yourDevice --run-time=100 --record-frequency=0.25 --test-name=burn-in-at-(-40)\n\n" + "Endurance testing :\n" + "\tvirtium save-smart-to-vtview-log /dev/yourDevice --run-time=100 --record-frequency=1 --test-name=Endurance-test-JEDEG-219-workload\n\n" + "Just logging :\n" + "\tvirtium save-smart-to-vtview-log /dev/yourDevice"; + + const char *run_time = "(optional) Number of hours to log data (default = 20 hours)"; + const char *freq = "(optional) How often you want to log SMART data (0.25 = 15' , 0.5 = 30' , 1 = 1 hour, 2 = 2 hours, etc.). Default = 10 hours."; + const char *output_file = "(optional) Name of the log file (give it a name that easy for you to remember what the test is). You can leave it blank too, we will take care it for you."; + const char *test_name = "(optional) Name of the test you are doing. We use this as part of the name of the log file."; + struct nvme_dev *dev; + + struct vtview_save_log_settings cfg = { + .run_time_hrs = 20, + .log_record_frequency_hrs = 10, + .output_file = NULL, + .test_name = NULL, + }; + + OPT_ARGS(opts) = { + OPT_DOUBLE("run-time", 'r', &cfg.run_time_hrs, run_time), + OPT_DOUBLE("freq", 'f', &cfg.log_record_frequency_hrs, freq), + OPT_FILE("output-file", 'o', &cfg.output_file, output_file), + OPT_STRING("test-name", 'n', "NAME", &cfg.test_name, test_name), + OPT_END() + }; + + vt_generate_vtview_log_file_name(vt_default_log_file_name); + + if (argc >= 2) { + if (strlen(argv[1]) > sizeof(path) - 1) { + printf("Filename too long\n"); + return -1; + } + strcpy(path, argv[1]); + } + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("Error parse and open (err = %d)\n", err); + return err; + } + + printf("Running...\n"); + printf("Collecting data for device %s\n", path); + printf("Running for %lf hour(s)\n", cfg.run_time_hrs); + printf("Logging SMART data for every %lf hour(s)\n", cfg.log_record_frequency_hrs); + + ret = vt_update_vtview_log_header(dev_fd(dev), path, &cfg); + if (ret) { + err = EINVAL; + dev_close(dev); + return err; + } + + total_time = cfg.run_time_hrs * (float)HOUR_IN_SECONDS; + freq_time = cfg.log_record_frequency_hrs * (float)HOUR_IN_SECONDS; + + if (!freq_time) + freq_time = 1; + + start_time = time(NULL); + end_time = start_time + total_time; + + fflush(stdout); + + while (1) { + cur_time = time(NULL); + if (cur_time >= end_time) + break; + + ret = vt_add_entry_to_log(dev_fd(dev), path, &cfg); + if (ret) { + printf("Cannot update driver log\n"); + break; + } + + remain_time = end_time - cur_time; + freq_time = MIN2(freq_time, remain_time); + sleep(freq_time); + fflush(stdout); + } + + dev_close(dev); + return err; +} + +static int vt_show_identify(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + int ret, err = 0; + struct nvme_id_ctrl ctrl; + struct nvme_dev *dev; + char *desc = "Parse identify data to json format\n\n" + "Typical usages:\n\n" + "virtium show-identify /dev/yourDevice\n"; + + OPT_ARGS(opts) = { + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("Error parse and open (err = %d)\n", err); + return err; + } + + ret = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (ret) { + printf("Cannot read identify device\n"); + dev_close(dev); + return -1; + } + + vt_process_string(ctrl.sn, sizeof(ctrl.sn)); + vt_process_string(ctrl.mn, sizeof(ctrl.mn)); + vt_parse_detail_identify(&ctrl); + + dev_close(dev); + return err; +} |