From: Hanumanthu H Date: Thu, 13 May 2021 08:06:40 +0000 (+0530) Subject: Micron plugin version 1.0.6 X-Git-Tag: v1.15~78 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=e400c105d95ea8bf87592a633fc4bd833cfb8cc3;p=users%2Fsagi%2Fnvme-cli.git Micron plugin version 1.0.6 --- diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index f76c0156..8e4b7cff 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -38,7 +38,7 @@ /* Plugin version major_number.minor_number.patch */ static const char *__version_major = "1"; static const char *__version_minor = "0"; -static const char *__version_patch = "5"; +static const char *__version_patch = "6"; /* supported models of micron plugin; new models should be added at the end * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! @@ -1056,11 +1056,12 @@ static void init_d0_log_page(__u8 *buf, __u8 nsze) /* OCP and Vendor specific log data format */ struct micron_vs_logpage { char *field; - int size; + int size; /* FB client spec version 1.0 sizes - M5410 models */ + int size2; /* FB client spec version 0.7 sizes - M5407 models */ } -/* Smart Health Log information as per OCP spec */ +/* Smart Health Log information as per OCP spec M51CX models */ ocp_c0_log_page[] = { - { "Physical Media Units Written", 16 }, + { "Physical Media Units Written", 16}, { "Physical Media Units Read", 16 }, { "Raw Bad User NAND Block Count", 6}, { "Normalized Bad User NAND Block Count", 2}, @@ -1096,43 +1097,56 @@ ocp_c0_log_page[] = { }, /* Vendor Specific Health Log information */ fb_log_page[] = { - { "Physical Media Units Written - TLC", 16 }, - { "Physical Media Units Written - SLC", 16 }, - { "Normalized Bad User NAND Block Count", 2}, - { "Raw Bad User NAND Block Count", 6}, - { "XOR Recovery Count", 8}, - { "Uncorrectable Read Error Count", 8}, - { "SSD End to End Corrected Errors", 8}, - { "SSD End to End Detected Counts", 4}, - { "SSD End to End Uncorrected Counts", 4}, - { "System data % life-used", 1}, - { "Minimum User Data Erase Count - TLC", 8}, - { "Maximum User Data Erase Count - TLC", 8}, - { "Minimum User Data Erase Count - SLC", 8}, - { "Maximum User Data Erase Count - SLC", 8}, - { "Normalized Program Fail Count", 2}, - { "Raw Program Fail Count", 6}, - { "Normalized Erase Fail Count", 2}, - { "Raw Erase Fail Count", 6}, - { "Pcie Correctable Error Count", 8}, - { "% Free Blocks (User)", 1}, - { "Security Version Number", 8}, - { "% Free Blocks (System)", 1}, - { "Dataset Management (Deallocate) Commands", 16}, - { "Incomplete TRIM Data", 8}, - { "% Age of Completed TRIM", 1}, - { "Background Back-Pressure Gauge", 1}, - { "Soft ECC Error Count", 8}, - { "Refresh Count", 8}, - { "Normalized Bad System NAND Block Count", 2}, - { "Raw Bad System NAND Block Count", 6}, - { "Endurance Estimate", 16}, - { "Thermal Throttling Count", 1}, - { "Thermal Throttling Status", 1}, - { "Unaligned I/O", 8}, - { "Physical Media Units Read", 16}, - { "Reserved", 279}, - { "Log Page Version", 2} + { "Physical Media Units Written - TLC", 16, 16 }, + { "Physical Media Units Written - SLC", 16, 16 }, + { "Normalized Bad User NAND Block Count", 2, 2}, + { "Raw Bad User NAND Block Count", 6, 6}, + { "XOR Recovery Count", 8, 8}, + { "Uncorrectable Read Error Count", 8, 8}, + { "SSD End to End Corrected Errors", 8, 8}, + { "SSD End to End Detected Counts", 4, 8}, + { "SSD End to End Uncorrected Counts", 4, 8}, + { "System data % life-used", 1, 1}, + { "Reserved", 0, 3}, + { "Minimum User Data Erase Count - TLC", 8, 8}, + { "Maximum User Data Erase Count - TLC", 8, 8}, + { "Average User Data Erase Count - TLC", 0, 8}, + { "Minimum User Data Erase Count - SLC", 8, 8}, + { "Maximum User Data Erase Count - SLC", 8, 8}, + { "Average User Data Erase Count - SLC", 0, 8}, + { "Normalized Program Fail Count", 2, 2}, + { "Raw Program Fail Count", 6, 6}, + { "Normalized Erase Fail Count", 2, 2}, + { "Raw Erase Fail Count", 6, 6}, + { "Pcie Correctable Error Count", 8, 8}, + { "% Free Blocks (User)", 1, 1}, + { "Reserved", 0, 3}, + { "Security Version Number", 8, 8}, + { "% Free Blocks (System)", 1, 1}, + { "Reserved", 0, 3}, + { "Dataset Management (Deallocate) Commands", 16, 16}, + { "Incomplete TRIM Data", 8, 8}, + { "% Age of Completed TRIM", 1, 2}, + { "Background Back-Pressure Gauge", 1, 1}, + { "Reserved", 0, 3}, + { "Soft ECC Error Count", 8, 8}, + { "Refresh Count", 8, 8}, + { "Normalized Bad System NAND Block Count", 2, 2}, + { "Raw Bad System NAND Block Count", 6, 6}, + { "Endurance Estimate", 16, 16}, + { "Thermal Throttling Count", 1, 1}, + { "Thermal Throttling Status", 1, 1}, + { "Unaligned I/O", 8, 8}, + { "Physical Media Units Read", 16, 16}, + { "Reserved", 279, 0}, + { "Log Page Version", 2, 0}, + { "READ CMDs exceeding threshold", 0, 4}, + { "WRITE CMDs exceeding threshold", 0, 4}, + { "TRIMs CMDs exceeding threshold", 0, 4}, + { "Reserved", 0, 4}, + { "Reserved", 0, 210}, + { "Log Page Version", 0, 2}, + { "Log Page GUID", 0, 16}, }; /* Common function to print Micron VS log pages */ @@ -1140,66 +1154,70 @@ static void print_micron_vs_logs( __u8 *buf, /* raw log data */ struct micron_vs_logpage *log_page, /* format of the data */ int field_count, /* log field count */ - struct json_object *stats /* json object to add fields */ + struct json_object *stats, /* json object to add fields */ + __u8 spec /* ocp spec index */ ) { __u64 lval_lo, lval_hi; __u32 ival; __u16 sval; __u8 cval, lval[8] = { 0 }; - int field, guid_index; + int field; int offset = 0; for (field = 0; field < field_count; field++) { char datastr[1024] = { 0 }; - if (log_page[field].size == 16) { - if (strstr(log_page[field].field, "GUID")) { - char *tmpstr = datastr; - tmpstr += sprintf(datastr, "0x"); - for(guid_index = 0; guid_index < 16; guid_index++) - tmpstr += sprintf(tmpstr, "%01X", buf[offset + guid_index]); + char *sfield = NULL; + int size = (spec == 0) ? log_page[field].size : log_page[field].size2; + if (size == 0) continue; + sfield = log_page[field].field; + if (size == 16) { + if (strstr(sfield, "GUID")) { + sprintf(datastr, "0x%lX%lX", + (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset + 8])), + (uint64_t)le64_to_cpu(*(uint64_t *)(&buf[offset]))); } else { lval_lo = *((__u64 *)(&buf[offset])); lval_hi = *((__u64 *)(&buf[offset + 8])); if (lval_hi) - sprintf(datastr, "0x%"PRIx64"_%"PRIx64"", + sprintf(datastr, "0x%"PRIx64"%016"PRIx64"", le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); else sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); } - } else if (log_page[field].size == 8) { + } else if (size == 8) { lval_lo = *((__u64 *)(&buf[offset])); sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } else if (log_page[field].size == 7) { + } else if (size == 7) { /* 7 bytes will be in little-endian format, with last byte as MSB */ memcpy(&lval[0], &buf[offset], 7); memcpy((void *)&lval_lo, lval, 8); sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } else if (log_page[field].size == 6) { + } else if (size == 6) { ival = *((__u32 *)(&buf[offset])); sval = *((__u16 *)(&buf[offset + 4])); lval_lo = (((__u64)sval << 32) | ival); sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } else if (log_page[field].size == 4) { + } else if (size == 4) { ival = *((__u32 *)(&buf[offset])); sprintf(datastr, "0x%x", le32_to_cpu(ival)); - } else if (log_page[field].size == 2) { + } else if (size == 2) { sval = *((__u16 *)(&buf[offset])); sprintf(datastr, "0x%04x", le16_to_cpu(sval)); - } else if (log_page[field].size == 1) { + } else if (size == 1) { cval = buf[offset]; sprintf(datastr, "0x%02x", cval); } else { sprintf(datastr, "0"); } - offset += log_page[field].size; + offset += size; /* do not print reserved values */ - if (strstr(log_page[field].field, "Reserved")) + if (strstr(sfield, "Reserved")) continue; if (stats != NULL) { - json_object_add_value_string(stats, log_page[field].field, datastr); + json_object_add_value_string(stats, sfield, datastr); } else { - printf("%-40s : %-4s\n", log_page[field].field, datastr); + printf("%-40s : %-4s\n", sfield, datastr); } } } @@ -1219,7 +1237,7 @@ static void print_smart_cloud_health_log(__u8 *buf, bool is_json) logPages); } - print_micron_vs_logs(buf, ocp_c0_log_page, field_count, stats); + print_micron_vs_logs(buf, ocp_c0_log_page, field_count, stats, 0); if (is_json) { json_array_add_value_object(logPages, stats); @@ -1229,7 +1247,7 @@ static void print_smart_cloud_health_log(__u8 *buf, bool is_json) } } -static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json) +static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json, __u8 spec) { struct json_object *root; struct json_object *logPages; @@ -1244,7 +1262,7 @@ static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json) logPages); } - print_micron_vs_logs(buf, fb_log_page, field_count, stats); + print_micron_vs_logs(buf, fb_log_page, field_count, stats, spec); /* print last three entries from D0 log page */ init_d0_log_page(buf2, nsze); @@ -1363,11 +1381,12 @@ static int micron_nand_stats(int argc, char **argv, if (nsze == 0 && nsze_from_oacs) nsze = ((ctrl.oacs >> 3) & 0x1); err = 0; - if (has_fb_log) - print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json); - else if (has_d0_log) + if (has_fb_log) { + __u8 spec = (eModel == M5410) ? 0 : 1; /* FB spec version */ + print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json, spec); + } else if (has_d0_log) { print_nand_stats_d0((__u8 *)extSmartLog, nsze, is_json); - else { + } else { printf("Unable to retrieve extended smart log for the drive\n"); err = -ENOTTY; } @@ -1483,6 +1502,62 @@ static void GetErrorlogData(int fd, int entries, const char *dir) free(error_log); } +static void GetGenericLogs(int fd, const char *dir) +{ + struct nvme_self_test_log self_test_log; + struct nvme_firmware_log_page fw_log; + struct nvme_effects_log_page effects; + struct nvme_persistent_event_log_head pevent_log_head; + void *pevent_log_info = NULL; + __u32 log_len = 0; + int err = 0 ; + bool huge = false; + + /* get self test log */ + if (nvme_self_test_log(fd, sizeof(self_test_log), &self_test_log) == 0) { + WriteData((__u8*)&self_test_log, sizeof(self_test_log), dir, + "drive_self_test.bin", "self test log"); + } + + /* get fw slot info log */ + if (nvme_fw_log(fd, &fw_log) == 0) { + WriteData((__u8*)&fw_log, sizeof(fw_log), dir, + "firmware_slot_info_log.bin", "firmware log"); + } + + /* get effects log */ + if (nvme_effects_log(fd, &effects) == 0) { + WriteData((__u8*)&effects, sizeof(effects), dir, + "command_effects_log.bin", "effects log"); + } + + /* get persistent event log */ + (void)nvme_persistent_event_log(fd, NVME_PEVENT_LOG_RELEASE_CTX, + sizeof(pevent_log_head), &pevent_log_head); + memset(&pevent_log_head, 0, sizeof(pevent_log_head)); + err = nvme_persistent_event_log(fd, NVME_PEVENT_LOG_EST_CTX_AND_READ, + sizeof(pevent_log_head), &pevent_log_head); + if (err) { + fprintf(stderr, "Failed to set persistent event log read context"); + return; + } + + log_len = le64_to_cpu(pevent_log_head.tll); + pevent_log_info = nvme_alloc(log_len, &huge); + if (!pevent_log_info) { + perror("could not alloc buffer for persistent event log page\n"); + return; + } + err = nvme_persistent_event_log(fd, NVME_PEVENT_LOG_READ, + log_len, pevent_log_info); + if (err == 0) { + WriteData((__u8*)pevent_log_info, log_len, dir, + "persistent_event_log.bin", "persistent event log"); + } + nvme_free(pevent_log_info, huge); + return; +} + static void GetNSIDDInfo(int fd, const char *dir, int nsid) { char file[PATH_MAX] = { 0 }; @@ -1537,14 +1612,14 @@ static void GetOSConfig(const char *strOSDirName) static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, int *logSize, int da) { - int err; + int err, bs = 512, offset = bs; unsigned short data_area[4]; unsigned char ctrl_init = (type == 0x8); - __u8 *buffer = (unsigned char *)calloc(512, 1); + __u8 *buffer = (unsigned char *)calloc(bs, 1); if (buffer == NULL) return -1; - err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, 512, 0); + err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, bs, 0); if (err != 0) { fprintf(stderr, "Failed to get telemetry log header for 0x%X\n", type); if (buffer != NULL) { @@ -1553,10 +1628,10 @@ static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, return err; } - // compute size of the log - data_area[1] = buffer[9] << 16 | buffer[8]; - data_area[2] = buffer[11] << 16 | buffer[10]; - data_area[3] = buffer[13] << 16 | buffer[12]; + /* compute size of the log */ + data_area[1] = buffer[9] << 8 | buffer[8]; + data_area[2] = buffer[11] << 8 | buffer[10]; + data_area[3] = buffer[13] << 8 | buffer[12]; data_area[0] = data_area[1] > data_area[2] ? data_area[1] : data_area[2]; data_area[0] = data_area[3] > data_area[0] ? data_area[3] : data_area[0]; @@ -1569,9 +1644,14 @@ static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, return -1; } - *logSize = data_area[da] * 512; + *logSize = data_area[da] * bs; + offset = bs; + err = 0; if ((buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize))) != NULL) { - err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, *logSize, 0); + while (err == 0 && offset != *logSize) { + err = nvme_get_telemetry_log(fd, buffer + offset, gen, ctrl_init, bs, offset); + offset += bs; + } } if (err == 0 && buffer != NULL) { @@ -1594,12 +1674,13 @@ static int GetTelemetryData(int fd, const char *dir) __u8 log; char *file; } tmap[] = { - {0x07, "nvme_host_telemetry.bin"}, - {0x08, "nvme_cntrl_telemetry.bin"}, + {0x07, "nvmetelemetrylog.bin"}, + {0x08, "nvmetelemetrylog.bin"}, }; for(i = 0; i < (int)(sizeof(tmap)/sizeof(tmap[0])); i++) { - err = micron_telemetry_log(fd, 0, tmap[i].log, &buffer, &logSize, 0); + err = micron_telemetry_log(fd, (tmap[i].log == 0x07), + tmap[i].log, &buffer, &logSize, 0); if (err == 0 && logSize > 0 && buffer != NULL) { sprintf(msg, "telemetry log: 0x%X", tmap[i].log); WriteData(buffer, logSize, dir, tmap[i].file, msg); @@ -1667,20 +1748,56 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, const char *desc = "Get drive HW information"; int fd, err = 0; struct nvme_id_ctrl ctrl = { 0 }; + struct nvme_admin_cmd admin_cmd = { 0 }; + struct fb_drive_info { + unsigned char hw_ver_major; + unsigned char hw_ver_minor; + unsigned char ftl_unit_size; + unsigned char bs_ver_major; + unsigned char bs_ver_minor; + } dinfo = { 0 }; + eDriveModel model = UNKNOWN_MODEL; OPT_ARGS(opts) = { OPT_END() }; - if ((fd = micron_parse_options(argc, argv, desc, opts, NULL)) < 0) + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) return err; - err = nvme_identify_ctrl(fd, &ctrl); - if (err) { - fprintf(stderr, "ERROR : nvme_identify_ctrl() failed with 0x%x\n", err); + if (model == UNKNOWN_MODEL) { + fprintf(stderr, "ERROR : Unsupported drive for vs-drive-info cmd"); return -1; } - printf("%u.%u\n", ctrl.vs[820], ctrl.vs[821]); + if (model == M5407) { + admin_cmd.opcode = 0xD4, + admin_cmd.addr = (__u64) (uintptr_t) &dinfo; + admin_cmd.data_len = (__u32)sizeof(dinfo); + admin_cmd.cdw12 = 3; + err = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &admin_cmd); + if (err) { + fprintf(stderr, "ERROR : drive-info opcode failed with 0x%x\n", err); + return -1; + } + printf("Drive Hardware Version: %hhu.%hhu\n", + dinfo.hw_ver_major, dinfo.hw_ver_minor); + + if (dinfo.ftl_unit_size) + printf("FTL_unit_size: %hhu KB\n", dinfo.ftl_unit_size); + + if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { + printf("Boot Spec.Version: %hhu.%hhu\n", + dinfo.bs_ver_major, dinfo.bs_ver_minor); + } + } else { + err = nvme_identify_ctrl(fd, &ctrl); + if (err) { + fprintf(stderr, "ERROR : identify_ctrl() failed with 0x%x\n", err); + return -1; + } + printf("Drive Hardware Version: %hhu.%hhu\n", ctrl.vs[820], ctrl.vs[821]); + } + return 0; } @@ -1825,8 +1942,7 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c int count = 0; unsigned int logC2[C2_log_size/sizeof(int)] = { 0 }; eDriveModel eModel = UNKNOWN_MODEL; - struct nvme_id_ctrl ctrl; - int fd, err, ctrlIdx; + int fd, err; struct format { char *fmt; }; @@ -1841,9 +1957,7 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c OPT_END() }; - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; + if ((fd = micron_parse_options(argc, argv, desc, opts, &eModel)) < 0) { return -1; } @@ -1853,16 +1967,8 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c return -1; } - err = nvme_identify_ctrl(fd, &ctrl); - if (err) { - fprintf(stderr, "failed get device identification data, error: %x\n", err); - goto out; - } - /* check if product supports fw_history log */ err = -EINVAL; - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - eModel = GetDriveModel(ctrlIdx); if (eModel != M51CX) { fprintf(stderr, "Unsupported drive model for vs-fw-activate-history command\n"); goto out; @@ -1919,8 +2025,7 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c const char *desc = "Retrieve Micron OCP Smart Health log for the given device "; unsigned int logC0[C0_log_size/sizeof(int)] = { 0 }; eDriveModel eModel = UNKNOWN_MODEL; - struct nvme_id_ctrl ctrl; - int fd, err, ctrlIdx; + int fd, err; bool is_json = false; struct format { char *fmt; @@ -1935,32 +2040,23 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c OPT_END() }; - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; + if ((fd = micron_parse_options(argc, argv, desc, opts, &eModel)) < 0) { return -1; } - if (strcmp(cfg.fmt, "json") == 0) - is_json = true; - - err = nvme_identify_ctrl(fd, &ctrl); - if (err) - goto out; - - /* pull log details based on the model name */ - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + err = -EINVAL; + /* check for models that support 0xC0 log */ + if (eModel != M51CX) { printf ("Unsupported drive model for vs-smart-add-log commmand\n"); close(fd); goto out; } - /* should check for firmware version if this log is supported or not */ - if (eModel == M5407 || eModel == M5410) { - err = nvme_get_log(fd, NVME_NSID_ALL, 0xC0, false, NVME_NO_LOG_LSP, - C0_log_size, logC0); - } + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + err = nvme_get_log(fd, NVME_NSID_ALL, 0xC0, false, NVME_NO_LOG_LSP, + C0_log_size, logC0); if (err < 0) { printf("Unable to retrieve extended smart log for the drive\n"); err = -ENOTTY; @@ -2249,6 +2345,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, GetSmartlogData(fd, strCtrlDirName); GetErrorlogData(fd, ctrl.elpe, strCtrlDirName); + GetGenericLogs(fd, strCtrlDirName); // pull if telemetry log data is supported if ((ctrl.lpa & 0x8) == 0x8) @@ -2256,7 +2353,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, GetFeatureSettings(fd, strCtrlDirName); - if (eModel != M5410) { + if (eModel != M5410 && eModel != M5407) { memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); if (eModel == M51AX) memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs));