diff options
Diffstat (limited to 'plugins/micron')
-rw-r--r-- | plugins/micron/micron-nvme.c | 688 | ||||
-rw-r--r-- | plugins/micron/micron-nvme.h | 7 |
2 files changed, 594 insertions, 101 deletions
diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 840682d..d333c4c 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -9,12 +9,13 @@ #include <string.h> #include <libgen.h> #include <sys/stat.h> +#include "common.h" #include "nvme.h" -#include "nvme-print.h" -#include "nvme-status.h" -#include "nvme-ioctl.h" -#include <sys/ioctl.h> +#include "libnvme.h" #include <limits.h> +#include "linux/types.h" +#include "nvme-print.h" + #define CREATE_CMD #include "micron-nvme.h" @@ -30,6 +31,7 @@ #define C2_log_size 4096 #define D0_log_size 512 #define FB_log_size 512 +#define E1_log_size 256 #define MaxLogChunk 16 * 1024 #define CommonChunkSize 16 * 4096 @@ -39,7 +41,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 = "6"; +static const char *__version_patch = "8"; /* supported models of micron plugin; new models should be added at the end * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! @@ -66,7 +68,7 @@ typedef struct _LogPageHeader_t { static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg) { - char tempFolder[PATH_MAX] = { 0 }; + char tempFolder[8192] = { 0 }; FILE *fpOutFile = NULL; sprintf(tempFolder, "%s/%s", dir, file); if ((fpOutFile = fopen(tempFolder, "ab+")) != NULL) { @@ -115,6 +117,7 @@ static eDriveModel GetDriveModel(int idx) } if (vendor_id == MICRON_VENDOR_ID) { switch (device_id) { + case 0x5196: case 0x51A0: case 0x51A1: case 0x51A2: @@ -311,8 +314,8 @@ static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) LogPageHeader_t *pLogHeader = NULL; if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { - err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, NVME_NO_LOG_LSP, - CommonChunkSize, pTmpBuf); + err = nvme_get_log_simple(nFD, ucLogID, + CommonChunkSize, pTmpBuf); if (err == 0) { pLogHeader = (LogPageHeader_t *) pTmpBuf; LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; @@ -334,7 +337,7 @@ static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize) { int err = 0; - struct nvme_admin_cmd cmd = { 0 }; + struct nvme_passthru_cmd cmd = { 0 }; unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); unsigned int uiMaxChunk = uiNumDwords; unsigned int uiNumChunks = 1; @@ -380,7 +383,7 @@ static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer cmd.addr = (__u64) (uintptr_t) pTempPtr; cmd.nsid = 0xFFFFFFFF; cmd.data_len = uiXferDwords * 4; - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); + err = nvme_submit_admin_passthru(nFD, &cmd, NULL); ullBytesRead += uiXferDwords * 4; pTempPtr = pBuffer + ullBytesRead; } @@ -422,8 +425,7 @@ static int GetCommonLogPage(int nFD, unsigned char ucLogID, goto exit_status; } memset(pTempPtr, 0, nBuffSize); - err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, NVME_NO_LOG_LSP, - nBuffSize, pTempPtr); + err = nvme_get_log_simple(nFD, ucLogID, nBuffSize, pTempPtr); *pBuffer = pTempPtr; exit_status: @@ -454,8 +456,8 @@ static int micron_parse_options(int argc, char **argv, const char *desc, static int micron_fw_commit(int fd, int select) { - struct nvme_admin_cmd cmd = { - .opcode = nvme_admin_activate_fw, + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fw_commit, .cdw10 = 8, .cdw12 = select, }; @@ -554,13 +556,21 @@ static int micron_selective_download(int argc, char **argv, while (fw_size > 0) { xfer = min(xfer, fw_size); - err = nvme_fw_download(fd, offset, xfer, fw_buf); + struct nvme_fw_download_args args = { + .args_size = sizeof(args), + .fd = fd, + .offset = offset, + .data_len = xfer, + .data = fw_buf, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = NULL, + }; + err = nvme_fw_download(&args); if (err < 0) { perror("fw-download"); goto out; } else if (err != 0) { - fprintf(stderr, "NVME Admin command error:%s(%x)\n", - nvme_status_to_string(err), err); + nvme_show_status(err); goto out; } fw_buf += xfer; @@ -584,7 +594,6 @@ static int micron_smbus_option(int argc, char **argv, struct command *cmd, struct plugin *plugin) { __u32 result = 0; - __u32 cdw10 = 0; __u32 cdw11 = 0; const char *desc = "Enable/Disable/Get status of SMBUS option on controller"; const char *option = "enable or disable or status"; @@ -627,7 +636,7 @@ static int micron_smbus_option(int argc, char **argv, if (!strcmp(opt.option, "enable")) { cdw11 = opt.value << 1 | 1; - err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, 0, &result); + err = nvme_set_features_simple(fd, fid, 1, cdw11, opt.save, &result); if (err == 0) { printf("successfully enabled SMBus on drive\n"); } else { @@ -635,8 +644,20 @@ static int micron_smbus_option(int argc, char **argv, } } else if (!strcmp(opt.option, "status")) { - cdw10 = opt.value; - err = nvme_get_feature(fd, 1, fid, cdw10, 0, 0, 0, 0, &result); + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = fid, + .nsid = 1, + .sel = opt.value, + .cdw11 = 0, + .uuidx = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_get_features(&args); if (err == 0) { printf("SMBus status on the drive: %s (returns %s temperature) \n", (result & 1) ? "enabled" : "disabled", @@ -647,7 +668,7 @@ static int micron_smbus_option(int argc, char **argv, } else if (!strcmp(opt.option, "disable")) { cdw11 = opt.value << 1 | 0; - err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, 0, &result); + err = nvme_set_features_simple(fd, fid, 1, cdw11, opt.save, &result); if (err == 0) { printf("Successfully disabled SMBus on drive\n"); } else { @@ -698,7 +719,7 @@ static int micron_temp_stats(int argc, char **argv, struct command *cmd, if (strcmp(cfg.fmt, "json") == 0) is_json = true; - err = nvme_smart_log(fd, 0xffffffff, &smart_log); + err = nvme_get_log_smart(fd, 0xffffffff, false, &smart_log); if (!err) { temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); temperature = temperature ? temperature - 273 : 0; @@ -933,11 +954,11 @@ static int micron_clear_pcie_correctable_errors(int argc, char **argv, /* For M51CX models, PCIe errors are cleared using 0xC3 feature */ if (model == M51CX) { - err = nvme_set_feature(fd, 0, fid, (1 << 31), 0, 0, 0, 0, 0, &result); + err = nvme_set_features_simple(fd, fid, 0, (1 << 31), false, &result); if (err == 0 && (err = (int)result) == 0) printf("Device correctable errors cleared!\n"); - else if (err > 0) - nvme_show_status(err); + else if (err > 0) + nvme_show_status(err); else printf("Error clearing Device correctable errors = 0x%x\n", err); goto out; @@ -1098,6 +1119,36 @@ ocp_c0_log_page[] = { { "Log Page Version", 2}, { "Log Page GUID", 16}, }, +/* Extended SMART log information */ +e1_log_page[] = { + { "Reserved", 12}, + { "Grown Bad Block Count", 4}, + { "Per Block Max Erase Count", 4}, + { "Power On Minutes", 4}, + { "Reserved", 24}, + { "Write Protect Reason", 4}, + { "Reserved", 12}, + { "Drive Capacity", 8}, + { "Reserved", 8}, + { "Total Erase Count", 8}, + { "Lifetime Use Rate", 8}, + { "Erase Fail Count", 8}, + { "Reserved", 8}, + { "Reported UC Errors", 8}, + { "Reserved", 24}, + { "Program Fail Count", 16}, + { "Total Bytes Read", 16}, + { "Total Bytes Written", 16}, + { "Reserved", 16}, + { "TU Size", 4}, + { "Total Block Stripe Count", 4}, + { "Free Block Stripe Count", 4}, + { "Block Stripe Size", 8}, + { "Reserved", 16}, + { "User Block Min Erase Count", 4}, + { "User Block Avg Erase Count", 4}, + { "User Block Max Erase Count", 4}, +}, /* Vendor Specific Health Log information */ fb_log_page[] = { { "Physical Media Units Written - TLC", 16, 16 }, @@ -1137,8 +1188,8 @@ fb_log_page[] = { { "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}, + { "Thermal Throttling Count", 1, 1}, { "Unaligned I/O", 8, 8}, { "Physical Media Units Read", 16, 16}, { "Reserved", 279, 0}, @@ -1272,13 +1323,13 @@ static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json, init_d0_log_page(buf2, nsze); if (is_json) { - for (int i = 0; i < 7; i++) { + for (int i = 4; i < 7; i++) { json_object_add_value_string(stats, - d0_log_page[i].field, - d0_log_page[i].datastr); + d0_log_page[i].field, + d0_log_page[i].datastr); } } else { - for (int i = 0; i < 7; i++) { + for (int i = 4; i < 7; i++) { printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); } } @@ -1367,42 +1418,115 @@ static int micron_nand_stats(int argc, char **argv, /* pull log details based on the model name */ sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + eModel = GetDriveModel(ctrlIdx); + if ((eModel == UNKNOWN_MODEL) || (eModel == M51CX)) { printf ("Unsupported drive model for vs-nand-stats command\n"); - err = -1; + err = -1; goto out; } - err = nvme_get_log(fd, NVME_NSID_ALL, 0xD0, false, NVME_NO_LOG_LSP, - D0_log_size, extSmartLog); + err = nvme_get_log_simple(fd, 0xD0, D0_log_size, extSmartLog); has_d0_log = (0 == err); /* 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, 0xFB, false, NVME_NO_LOG_LSP, - FB_log_size, logFB); + if (eModel == M5407 || eModel == M5410) { + err = nvme_get_log_simple(fd, 0xFB, FB_log_size, logFB); has_fb_log = (0 == err); } nsze = (ctrl.vs[987] == 0x12); - if (nsze == 0 && nsze_from_oacs) nsze = ((ctrl.oacs >> 3) & 0x1); - + err = 0; 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); - err = 0; + } else { + printf("Unable to retrieve extended smart log for the drive\n"); + err = -ENOTTY; } out: close(fd); if (err > 0) - nvme_show_status(err); - return nvme_status_to_errno(err, false); + nvme_show_status(err); + + return err; } +static void print_ext_smart_logs_e1(__u8 *buf, bool is_json) +{ + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = sizeof(e1_log_page)/sizeof(e1_log_page[0]); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "SMART Extended Log:0xE1", logPages); + } + else { + printf("SMART Extended Log:0xE1\n"); + } + + print_micron_vs_logs(buf, e1_log_page, field_count, stats, 0); + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } +} + +static int micron_smart_ext_log(int argc, char **argv, + struct command *cmd, struct plugin *plugin) +{ + const char *desc = "Retrieve extended SMART logs for the given device "; + unsigned int extSmartLog[E1_log_size/sizeof(int)] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + int fd = 0, err = 0, ctrlIdx = 0; + bool is_json = true; + struct format { + char *fmt; + }; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + if (strcmp(cfg.fmt, "normal") == 0) + is_json = false; + + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) != M51CX) { + printf ("Unsupported drive model for vs-smart-ext-log command\n"); + err = -1; + goto out; + } + err = nvme_get_log_simple(fd, 0xE1, E1_log_size, extSmartLog); + if (!err) { + print_ext_smart_logs_e1((__u8 *)extSmartLog, is_json); + } + +out: + close(fd); + if (err > 0) + nvme_show_status(err); + return err; +} static void GetDriveInfo(const char *strOSDirName, int nFD, struct nvme_id_ctrl *ctrlp) @@ -1487,7 +1611,7 @@ static void GetCtrlIDDInfo(const char *dir, struct nvme_id_ctrl *ctrlp) static void GetSmartlogData(int fd, const char *dir) { struct nvme_smart_log smart_log; - if (nvme_smart_log(fd, -1, &smart_log) == 0) { + if (nvme_get_log_smart(fd, -1, false, &smart_log) == 0) { WriteData((__u8*)&smart_log, sizeof(smart_log), dir, "smart_data.bin", "smart log"); } @@ -1502,7 +1626,7 @@ static void GetErrorlogData(int fd, int entries, const char *dir) if (error_log == NULL) return; - if (nvme_error_log(fd, entries, error_log) == 0) { + if (nvme_get_log_error(fd, entries, false, error_log) == 0) { WriteData((__u8*)error_log, logSize, dir, "error_information_log.bin", "error log"); } @@ -1513,50 +1637,50 @@ static void GetErrorlogData(int fd, int entries, const char *dir) 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; + struct nvme_firmware_slot fw_log; + struct nvme_cmd_effects_log effects; + struct nvme_persistent_event_log pevent_log; 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) { + if (nvme_get_log_device_self_test(fd, &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) { + if (nvme_get_log_fw_slot(fd, 1, &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) { + if (nvme_get_log_cmd_effects(fd, NVME_CSI_NVM, &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); + (void)nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_RELEASE_CTX, + sizeof(pevent_log), &pevent_log); + memset(&pevent_log, 0, sizeof(pevent_log)); + err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_EST_CTX_AND_READ, + sizeof(pevent_log), &pevent_log); if (err) { - fprintf(stderr, "Setting persistent event log read ctx failed (ignored)!\n"); + fprintf(stderr, "Failed to set persistent event log read context"); return; } - log_len = le64_to_cpu(pevent_log_head.tll); + log_len = le64_to_cpu(pevent_log.tll); pevent_log_info = nvme_alloc(log_len, &huge); if (!pevent_log_info) { - perror("could not alloc buffer for persistent event log page (ignored)!\n"); + perror("could not alloc buffer for persistent event log page\n"); return; } - err = nvme_persistent_event_log(fd, NVME_PEVENT_LOG_READ, + err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_READ, log_len, pevent_log_info); if (err == 0) { WriteData((__u8*)pevent_log_info, log_len, dir, @@ -1571,7 +1695,7 @@ static void GetNSIDDInfo(int fd, const char *dir, int nsid) char file[PATH_MAX] = { 0 }; struct nvme_id_ns ns; - if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) { + if (nvme_identify_ns(fd, nsid, &ns) == 0) { sprintf(file, "identify_namespace_%d_data.bin", nsid); WriteData((__u8*)&ns, sizeof(ns), dir, file, "id-ns"); } @@ -1617,7 +1741,7 @@ static void GetOSConfig(const char *strOSDirName) } } -static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, +static int micron_telemetry_log(int fd, __u8 type, __u8 **data, int *logSize, int da) { int err, bs = 512, offset = bs; @@ -1627,7 +1751,10 @@ static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, __u8 *buffer = (unsigned char *)calloc(bs, 1); if (buffer == NULL) return -1; - err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, bs, 0); + if (ctrl_init) + err = nvme_get_log_telemetry_ctrl(fd, true, 0, bs, buffer); + else + err = nvme_get_log_telemetry_host(fd, 0, bs, buffer); if (err != 0) { fprintf(stderr, "Failed to get telemetry log header for 0x%X\n", type); if (buffer != NULL) { @@ -1657,7 +1784,10 @@ static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, err = 0; if ((buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize))) != NULL) { while (err == 0 && offset != *logSize) { - err = nvme_get_telemetry_log(fd, buffer + offset, gen, ctrl_init, bs, offset); + if (ctrl_init) + err = nvme_get_log_telemetry_ctrl(fd, true, 0, *logSize, buffer + offset); + else + err = nvme_get_log_telemetry_host(fd, 0, *logSize, buffer + offset); offset += bs; } } @@ -1687,8 +1817,7 @@ static int GetTelemetryData(int fd, const char *dir) }; for(i = 0; i < (int)(sizeof(tmap)/sizeof(tmap[0])); i++) { - err = micron_telemetry_log(fd, (tmap[i].log == 0x07), - tmap[i].log, &buffer, &logSize, 0); + err = micron_telemetry_log(fd, 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); @@ -1735,7 +1864,20 @@ static int GetFeatureSettings(int fd, const char *dir) bufp = NULL; } - err = nvme_get_feature(fd, 1, fmap[i].id, 0, 0x0, 0, len, bufp, &attrVal); + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = fmap[i].id, + .nsid = 1, + .sel = 0, + .cdw11 = 0x0, + .uuidx = 0, + .data_len = len, + .data = bufp, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &attrVal, + }; + err = nvme_get_features(&args); if (err == 0) { sprintf(msg, "feature: 0x%X", fmap[i].id); WriteData((__u8*)&attrVal, sizeof(attrVal), dir, fmap[i].file, msg); @@ -1743,7 +1885,7 @@ static int GetFeatureSettings(int fd, const char *dir) WriteData(bufp, len, dir, fmap[i].file, msg); } } else { - fprintf(stderr, "Feature 0x%x data not retrieved, error %d (ignored)!\n", + printf("Feature 0x%x data not retrieved, error %d (ignored)!\n", fmap[i].id, err); errcnt++; } @@ -1757,7 +1899,7 @@ 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 nvme_passthru_cmd admin_cmd = { 0 }; struct fb_drive_info { unsigned char hw_ver_major; unsigned char hw_ver_minor; @@ -1794,11 +1936,11 @@ static int micron_drive_info(int argc, char **argv, struct command *cmd, is_json = true; if (model == M5407) { - admin_cmd.opcode = 0xDA, + 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); + err = nvme_submit_admin_passthru(fd, &admin_cmd, NULL); if (err) { fprintf(stderr, "ERROR : drive-info opcode failed with 0x%x\n", err); return -1; @@ -2025,7 +2167,7 @@ static int micron_fw_activation_history(int argc, char **argv, struct command *c goto out; } - err = nvme_get_log(fd, NVME_NSID_ALL, 0xC2, false, NVME_NO_LOG_LSP, C2_log_size, logC2); + err = nvme_get_log_simple(fd, 0xC2, C2_log_size, logC2); if (err) { fprintf(stderr, "Failed to retrieve fw activation history log, error: %x\n", err); goto out; @@ -2063,11 +2205,320 @@ out: return err; } -static int micron_error_reason(int argc, char **argv, struct command *cmd, +#define MICRON_FID_LATENCY_MONITOR 0xD0 +#define MICRON_LOG_LATENCY_MONITOR 0xD1 + +static int micron_latency_stats_track(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - printf("This command is not implemented for the drive\n"); - return 0; + int err = 0; + __u32 result = 0; + const char *desc = "Enable, Disable or Get cmd latency monitoring stats"; + const char *option = "enable or disable or status, default is status"; + const char *command = "commands to monitor for - all|read|write|trim," + " default is all i.e, enabled for all commands"; + const char *thrtime = "The threshold value to use for latency monitoring in" + " milliseconds, default is 800ms"; + + int fd = 0; + int fid = MICRON_FID_LATENCY_MONITOR; + eDriveModel model = UNKNOWN_MODEL; + uint32_t command_mask = 0x7; /* 1:read 2:write 4:trim 7:all */ + uint32_t timing_mask = 0x08080800; /* R[31-24]:W[23:16]:T[15:8]:0 */ + uint32_t enable = 2; + struct { + char *option; + char *command; + uint32_t threshold; + } opt = { + .option = "status", + .command = "all", + .threshold = 0 + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_STRING("command", 'c', "command", &opt.command, command), + OPT_UINT("threshold", 't', &opt.threshold, thrtime), + OPT_END() + }; + + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) { + return -1; + } + + if (!strcmp(opt.option, "enable")) { + enable = 1; + } else if (!strcmp(opt.option, "disable")) { + enable = 0; + } else if (strcmp(opt.option, "status")) { + printf("Invalid control option %s specified\n", opt.option); + return -1; + } + + struct nvme_get_features_args g_args = { + .args_size = sizeof(g_args), + .fd = fd, + .fid = fid, + .nsid = 0, + .sel = 0, + .cdw11 = 0, + .uuidx = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + + err = nvme_get_features(&g_args); + if (err != 0) { + printf("Failed to retrieve latency monitoring feature status\n"); + return err; + } + + /* If it is to retrieve the status only */ + if (enable == 2) { + printf("Latency Tracking Statistics is currently %s", + (result & 0xFFFF0000) ? "enabled" : "disabled"); + if ((result & 7) == 7) { + printf(" for All commands\n"); + } else if ((result & 7) > 0) { + printf(" for"); + if (result & 1) { + printf(" Read"); + } + if (result & 2) { + printf(" Write"); + } + if (result & 4) { + printf(" Trim"); + } + printf(" commands\n"); + } else if (result == 0) { + printf("\n"); + } + return err; + } + + /* read and validate threshold values if enable option is specified */ + if (enable == 1) { + if (opt.threshold > 2550) { + printf("The maximum threshold value cannot be more than 2550 ms\n"); + return -1; + } + /* timing mask is in terms of 10ms units, so min allowed is 10ms */ + else if ((opt.threshold % 10) != 0) { + printf("The threshold value should be multiple of 10 ms\n"); + return -1; + } + opt.threshold /= 10; + } + + /* read-in command(s) to be monitored */ + if (!strcmp(opt.command, "read")) { + command_mask = 0x1; + timing_mask = (opt.threshold << 24); + } else if (!strcmp(opt.command, "write")) { + command_mask = 0x2; + timing_mask = (opt.threshold << 16); + } else if (!strcmp(opt.command, "read")) { + command_mask = 0x4; + timing_mask = (opt.threshold << 8); + } else if (strcmp(opt.command, "all")) { + printf("Invalid command %s specified for option %s\n", + opt.command, opt.option); + return -1; + } + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = MICRON_FID_LATENCY_MONITOR, + .nsid = 0, + .cdw11 = enable, + .cdw12 = command_mask, + .save = 1, + .uuidx = 0, + .cdw13 = timing_mask, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_set_features(&args); + if (err == 0) { + printf("Successfully %sed latency monitoring for %s commands\n", + opt.option, opt.command); + } else { + printf("Failed to %s latency monitoring for %s commands\n", + opt.option, opt.command); + } + + close(fd); + return err; +} + + +static int micron_latency_stats_logs(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ +#define LATENCY_LOG_ENTRIES 16 + struct latency_log_entry { + uint64_t timestamp; + uint32_t latency; + uint32_t cmdtag; + union { + struct { + uint32_t opcode:8; + uint32_t fuse:2; + uint32_t rsvd1:4; + uint32_t psdt:2; + uint32_t cid:16; + }; + uint32_t dw0; + }; + uint32_t nsid; + uint32_t slba_low; + uint32_t slba_high; + union { + struct { + uint32_t nlb:16; + uint32_t rsvd2:9; + uint32_t deac:1; + uint32_t prinfo:4; + uint32_t fua:1; + uint32_t lr:1; + }; + uint32_t dw12; + }; + uint32_t dsm; + uint32_t rfu[6]; + } log[LATENCY_LOG_ENTRIES]; + eDriveModel model = UNKNOWN_MODEL; + int err = -1; + int fd = -1; + const char *desc = "Display Latency tracking log information"; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + memset(&log, 0, sizeof(log)); + err = nvme_get_log_simple(fd, 0xD1, sizeof(log), &log); + if (err) { + if (err < 0) + printf("Unable to retrieve latency stats log the drive\n"); + return err; + } + /* print header and each log entry */ + printf("Timestamp, Latency, CmdTag, Opcode, Fuse, Psdt,Cid, Nsid," + "Slba_L, Slba_H, Nlb, DEAC, PRINFO, FUA,LR\n"); + for (int i = 0; i < LATENCY_LOG_ENTRIES; i++) { + printf("%"PRIu64",%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n", + log[i].timestamp,log[i].latency, log[i].cmdtag, log[i].opcode, + log[i].fuse, log[i].psdt, log[i].cid, log[i].nsid, + log[i].slba_low, log[i].slba_high, log[i].nlb, + log[i].deac, log[i].prinfo, log[i].fua, log[i].lr); + } + printf("\n"); + return err; +} + +static int micron_latency_stats_info(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + const char *desc = "display command latency statistics"; + const char *command = "command to display stats - all|read|write|trim" + "default is all"; + int err = 0; + int fd = -1; + eDriveModel model = UNKNOWN_MODEL; + #define LATENCY_BUCKET_COUNT 32 + struct micron_latency_stats { + uint64_t version; /* major << 32 | minior */ + uint64_t all_cmds[LATENCY_BUCKET_COUNT]; + uint64_t read_cmds[LATENCY_BUCKET_COUNT]; + uint64_t write_cmds[LATENCY_BUCKET_COUNT]; + uint64_t trim_cmds[LATENCY_BUCKET_COUNT]; + uint32_t reserved[765]; /* round up to 4K */ + } log; + + struct latency_thresholds { + uint32_t start; + uint32_t end; + char *unit; + } thresholds[LATENCY_BUCKET_COUNT] = { + {0, 50, "us"}, {50, 100, "us"}, {100, 150, "us"}, {150, 200, "us"}, + {200, 300, "us"}, {300, 400, "us"}, {400, 500, "us"}, {500, 600, "us"}, + {600, 700, "us"}, {700, 800, "us"}, {800, 900, "us"}, {900, 1000, "us"}, + {1, 5, "ms"}, {5, 10, "ms"}, {10, 20, "ms"}, {20, 50, "ms"}, {50, 100, "ms"}, + {100, 200, "ms"}, {200, 300, "ms"}, {300, 400, "ms"}, {400, 500, "ms"}, + {500, 600, "ms"}, {600, 700, "ms"}, {700, 800, "ms"}, {800, 900, "ms"}, + {900, 1000, "ms"}, {1, 2, "s"}, {2, 3, "s"}, {3, 4, "s"}, {4, 5, "s"}, + {5,8, "s"}, + {8, INT_MAX, "s"}, + }; + + struct { + char *command; + } opt = { + .command="all" + }; + + uint64_t *cmd_stats = &log.all_cmds[0]; + char *cmd_str = "All"; + + OPT_ARGS(opts) = { + OPT_STRING("command", 'c', "command", &opt.command, command), + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + if (!strcmp(opt.command, "read")) { + cmd_stats = &log.read_cmds[0]; + cmd_str = "Read"; + } else if (!strcmp(opt.command, "write")) { + cmd_stats = &log.write_cmds[0]; + cmd_str = "Write"; + } else if (!strcmp(opt.command, "trim")) { + cmd_stats = &log.trim_cmds[0]; + cmd_str = "Trim"; + } else if (strcmp(opt.command, "all")) { + printf("Invalid command option %s to display latency stats\n", opt.command); + return -1; + } + + memset(&log, 0, sizeof(log)); + err = nvme_get_log_simple(fd, 0xD0, sizeof(log), &log); + if (err) { + if (err < 0) + printf("Unable to retrieve latency stats log the drive\n"); + return err; + } + printf("Micron IO %s Command Latency Statistics\n" + "Major Revision : %d\nMinor Revision : %d\n", + cmd_str, (int)(log.version >> 32), (int)(log.version & 0xFFFFFFFF)); + printf("=============================================\n"); + printf("Bucket Start End Command Count\n"); + printf("=============================================\n"); + + for (int b = 0; b < LATENCY_BUCKET_COUNT; b++) { + int bucket = b + 1; + char start[32] = { 0 }; + char end[32] = { 0 }; + sprintf(start, "%u%s", thresholds[b].start, thresholds[b].unit); + if (thresholds[b].end == INT_MAX) + sprintf(end, "INF"); + else + sprintf(end, "%u%s", thresholds[b].end, thresholds[b].unit); + printf("%2d %8s %8s %8"PRIu64"\n", + bucket, start, end, cmd_stats[b]); + } + return err; } static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *cmd, @@ -2106,8 +2557,8 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c __u8 nsze; if ((err = nvme_identify_ctrl(fd, &ctrl)) == 0) - err = nvme_get_log(fd, NVME_NSID_ALL, 0xFB, false, NVME_NO_LOG_LSP, - FB_log_size, logFB); + err = nvme_get_log_simple(fd, 0xFB, + FB_log_size, logFB); if (err) { if (err < 0) printf("Unable to retrieve smart log 0xFB for the drive\n"); @@ -2128,8 +2579,7 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c goto out; } - err = nvme_get_log(fd, NVME_NSID_ALL, 0xC0, false, NVME_NO_LOG_LSP, - C0_log_size, logC0); + err = nvme_get_log_simple(fd, 0xC0, C0_log_size, logC0); if (err == 0) { print_smart_cloud_health_log((__u8 *)logC0, is_json); } else if (err < 0) { @@ -2138,8 +2588,8 @@ static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *c out: close(fd); if (err > 0) - nvme_show_status(err); - return nvme_status_to_errno(err, false); + nvme_show_status(err); + return err; } static int micron_clr_fw_activation_history(int argc, char **argv, @@ -2163,8 +2613,7 @@ static int micron_clr_fw_activation_history(int argc, char **argv, return err; } - //err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); - err = nvme_set_feature(fd, 1, fid, 0, 0, 0, 0, 0, 0, &result); + err = nvme_set_features_simple(fd, fid, 1, 0, 0, &result); if (err == 0) err = (int)result; return err; } @@ -2210,22 +2659,64 @@ static int micron_telemetry_cntrl_option(int argc, char **argv, } if (!strcmp(opt.option, "enable")) { - err = nvme_set_feature(fd, 1, fid, 1, 0, (opt.select & 0x1), 0, 0, 0, &result); + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = fid, + .nsid = 1, + .cdw11 = 1, + .cdw12 = 0, + .save = (opt.select & 0x1), + .uuidx = 0, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_set_features(&args); if (err == 0) { printf("successfully set controller telemetry option\n"); } else { printf("Failed to set controller telemetry option\n"); } } else if (!strcmp(opt.option, "disable")) { - err = nvme_set_feature(fd, 1, fid, 0, 0, (opt.select & 0x1), 0, 0, 0, &result); + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = fid, + .nsid = 1, + .cdw11 = 0, + .cdw12 = 0, + .save = (opt.select & 0x1), + .uuidx = 0, + .cdw15 = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_set_features(&args); if (err == 0) { printf("successfully disabled controller telemetry option\n"); } else { printf("Failed to disable controller telemetry option\n"); } } else if (!strcmp(opt.option, "status")) { - opt.select &= 0x3; - err = nvme_get_feature(fd, 1, fid, opt.select, 0, 0, 0, 0, &result); + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = fd, + .fid = fid, + .nsid = 1, + .sel = opt.select & 0x3, + .cdw11 = 0, + .uuidx = 0, + .data_len = 0, + .data = NULL, + .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, + .result = &result, + }; + err = nvme_get_features(&args); if (err == 0) { printf("Controller telemetry option : %s\n", (result) ? "enabled" : "disabled"); @@ -2385,7 +2876,7 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, goto out; } int logSize = 0; __u8 *buffer = NULL; const char *dir = "."; - err = micron_telemetry_log(fd, 0, cfg.log, &buffer, &logSize, cfg.data_area); + err = micron_telemetry_log(fd, cfg.log, &buffer, &logSize, cfg.data_area); if (err == 0 && logSize > 0 && buffer != NULL) { sprintf(msg, "telemetry log: 0x%X", cfg.log); WriteData(buffer, logSize, dir, cfg.package, msg); @@ -2397,10 +2888,10 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, printf("Preparing log package. This will take a few seconds...\n"); - // trim spaces out of serial number string */ + /* trim spaces out of serial number string */ int i, j = 0; for (i = 0; i < sizeof(ctrl.sn); i++) { - if (isblank(ctrl.sn[i])) + if (isblank((int)ctrl.sn[i])) continue; sn[j++] = ctrl.sn[i]; } @@ -2420,8 +2911,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 + /* pull if telemetry log data is supported */ if ((ctrl.lpa & 0x8) == 0x8) GetTelemetryData(fd, strCtrlDirName); @@ -2465,8 +2955,8 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, if (eModel == M5410 || eModel == M5407) err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); else - err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, - false, NVME_NO_LOG_LSP, bSize, dataBuffer); + err = nvme_get_log_simple(fd, aVendorLogs[i].ucLogPage, + bSize, dataBuffer); } break; @@ -2484,14 +2974,14 @@ static int micron_internal_logs(int argc, char **argv, struct command *cmd, break; } memset(dataBuffer, 0, bSize); - err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, - false, NVME_NO_LOG_LSP, bSize, dataBuffer); + err = nvme_get_log_simple(fd, aVendorLogs[i].ucLogPage, + bSize, dataBuffer); maxSize = aVendorLogs[i].nMaxSize - bSize; while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); - err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, - false, NVME_NO_LOG_LSP, bSize, dataBuffer); + err = nvme_get_log_simple(fd, aVendorLogs[i].ucLogPage, + bSize, dataBuffer); if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) break; maxSize -= bSize; diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h index be80544..c1b224e 100644 --- a/plugins/micron/micron-nvme.h +++ b/plugins/micron/micron-nvme.h @@ -13,14 +13,17 @@ PLUGIN(NAME("micron", "Micron vendor specific extensions", NVME_VERSION), ENTRY("vs-pcie-stats", "Retrieve Micron PCIe error stats", micron_pcie_stats) ENTRY("clear-pcie-correctable-errors", "Clear correctable PCIe errors", micron_clear_pcie_correctable_errors) ENTRY("vs-internal-log", "Retrieve Micron logs", micron_internal_logs) - ENTRY("vs-telemetry-controller-option", "Enable/Disable controller telemetry log generation", micron_telemetry_cntrl_option) + ENTRY("vs-telemetry-controller-option", "Enable/Disable controller telemetry log generation", micron_telemetry_cntrl_option) ENTRY("vs-nand-stats", "Retrieve NAND Stats", micron_nand_stats) + ENTRY("vs-smart-ext-log", "Retrieve extended SMART logs", micron_smart_ext_log) ENTRY("vs-drive-info", "Retrieve Drive information", micron_drive_info) ENTRY("plugin-version", "Display plugin version info", micron_plugin_version) ENTRY("cloud-SSD-plugin-version", "Display plugin version info", micron_cloud_ssd_plugin_version) ENTRY("log-page-directory", "Retrieve log page directory", micron_logpage_dir) ENTRY("vs-fw-activate-history", "Display FW activation history", micron_fw_activation_history) - ENTRY("vs-error-reason-identifier", "Retrieve Error reason", micron_error_reason) + ENTRY("latency-tracking", "Latency monitoring feature control", micron_latency_stats_track) + ENTRY("latency-stats", "Latency information for tracked commands", micron_latency_stats_info) + ENTRY("latency-logs", "Latency log details tracked by drive", micron_latency_stats_logs) ENTRY("vs-smart-add-log", "Retrieve extended SMART data", micron_ocp_smart_health_logs) ENTRY("clear-fw-activate-history", "Clear FW activation history", micron_clr_fw_activation_history) ENTRY("vs-smbus-option", "Enable/Disable SMBUS on the drive", micron_smbus_option) |