summaryrefslogtreecommitdiffstats
path: root/plugins/micron/micron-nvme.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/micron/micron-nvme.c')
-rw-r--r--plugins/micron/micron-nvme.c688
1 files changed, 589 insertions, 99 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;