diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-06-30 22:38:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2023-06-30 22:38:51 +0000 |
commit | 5d64e8a26388e2abbf6a6585d17392d6e944ae7b (patch) | |
tree | 4eae47918df5f83f14e05bede9c361237a7dc089 /plugins/micron/micron-nvme.c | |
parent | Releasing debian version 2.4+really2.4-3. (diff) | |
download | nvme-cli-5d64e8a26388e2abbf6a6585d17392d6e944ae7b.tar.xz nvme-cli-5d64e8a26388e2abbf6a6585d17392d6e944ae7b.zip |
Merging upstream version 2.5.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/micron/micron-nvme.c')
-rw-r--r-- | plugins/micron/micron-nvme.c | 5921 |
1 files changed, 2958 insertions, 2963 deletions
diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index bd5f7d7..d6fb601 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -21,10 +21,10 @@ #include "micron-nvme.h" /* Supported Vendor specific feature ids */ -#define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS 0xC3 -#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xC1 -#define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION 0xCF -#define MICRON_FEATURE_SMBUS_OPTION 0xD5 +#define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS 0xC3 +#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY 0xC1 +#define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION 0xCF +#define MICRON_FEATURE_SMBUS_OPTION 0xD5 /* Supported Vendor specific log page sizes */ #define C5_log_size (((452 + 16 * 1024) / 4) * 4096) @@ -33,8 +33,8 @@ #define D0_log_size 512 #define FB_log_size 512 #define E1_log_size 256 -#define MaxLogChunk 16 * 1024 -#define CommonChunkSize 16 * 4096 +#define MaxLogChunk (16 * 1024) +#define CommonChunkSize (16 * 4096) #define min(x, y) ((x) > (y) ? (y) : (x)) #define SensorCount 8 @@ -44,10 +44,19 @@ static const char *__version_major = "1"; static const char *__version_minor = "0"; static const char *__version_patch = "14"; -/* supported models of micron plugin; new models should be added at the end +/* + * supported models of micron plugin; new models should be added at the end * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! */ -typedef enum { M5410 = 0, M51AX, M51BX, M51CX, M5407, M5411, UNKNOWN_MODEL } eDriveModel; +enum eDriveModel { + M5410 = 0, + M51AX, + M51BX, + M51CX, + M5407, + M5411, + UNKNOWN_MODEL +}; #define MICRON_VENDOR_ID 0x1344 @@ -58,396 +67,398 @@ static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; static unsigned short vendor_id; static unsigned short device_id; -typedef struct _LogPageHeader_t { - unsigned char numDwordsInLogPageHeaderLo; - unsigned char logPageHeaderFormatVersion; - unsigned char logPageId; - unsigned char numDwordsInLogPageHeaderHi; - unsigned int numValidDwordsInPayload; - unsigned int numDwordsInEntireLogPage; -} LogPageHeader_t; +struct LogPageHeader_t { + unsigned char numDwordsInLogPageHeaderLo; + unsigned char logPageHeaderFormatVersion; + unsigned char logPageId; + unsigned char numDwordsInLogPageHeaderHi; + unsigned int numValidDwordsInPayload; + unsigned int numDwordsInEntireLogPage; +}; static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg) { - char tempFolder[8192] = { 0 }; - FILE *fpOutFile = NULL; - sprintf(tempFolder, "%s/%s", dir, file); - if ((fpOutFile = fopen(tempFolder, "ab+")) != NULL) { - if (fwrite(data, 1, len, fpOutFile) != len) { - printf("Failed to write %s data to %s\n", msg, tempFolder); - } - fclose(fpOutFile); - } else { - printf("Failed to open %s file to write %s\n", tempFolder, msg); - } + char tempFolder[8192] = { 0 }; + FILE *fpOutFile = NULL; + + sprintf(tempFolder, "%s/%s", dir, file); + fpOutFile = fopen(tempFolder, "ab+"); + if (fpOutFile) { + if (fwrite(data, 1, len, fpOutFile) != len) + printf("Failed to write %s data to %s\n", msg, tempFolder); + fclose(fpOutFile); + } else { + printf("Failed to open %s file to write %s\n", tempFolder, msg); + } } static int ReadSysFile(const char *file, unsigned short *id) { - int ret = 0; - char idstr[32] = { '\0' }; - int fd = open(file, O_RDONLY); - - if (fd < 0) { - perror(file); - return fd; - } - - ret = read(fd, idstr, sizeof(idstr)); - close(fd); - if (ret < 0) - perror("read"); - else - *id = strtol(idstr, NULL, 16); - - return ret; + int ret = 0; + char idstr[32] = { '\0' }; + int fd = open(file, O_RDONLY); + + if (fd < 0) { + perror(file); + return fd; + } + + ret = read(fd, idstr, sizeof(idstr)); + close(fd); + if (ret < 0) + perror("read"); + else + *id = strtol(idstr, NULL, 16); + + return ret; } -static eDriveModel GetDriveModel(int idx) +static enum eDriveModel GetDriveModel(int idx) { - eDriveModel eModel = UNKNOWN_MODEL; - char path[512]; - - sprintf(path, fvendorid1, idx); - if (ReadSysFile(path, &vendor_id) < 0) { - sprintf(path, fvendorid2, idx); - ReadSysFile(path, &vendor_id); - } - sprintf(path, fdeviceid1, idx); - if (ReadSysFile(path, &device_id) < 0) { - sprintf(path, fdeviceid2, idx); - ReadSysFile(path, &device_id); - } - if (vendor_id == MICRON_VENDOR_ID) { - switch (device_id) { - case 0x5196: - case 0x51A0: - case 0x51A1: - case 0x51A2: - eModel = M51AX; - break; - case 0x51B0: - case 0x51B1: - case 0x51B2: - eModel = M51BX; - break; - case 0x51C0: - case 0x51C1: - case 0x51C2: - case 0x51C3: - eModel = M51CX; - break; - case 0x5405: - case 0x5406: - case 0x5407: - eModel = M5407; - break; - case 0x5410: - eModel = M5410; - break; - case 0x5411: - eModel = M5411; - break; - default: - break; - } - } - return eModel; + enum eDriveModel eModel = UNKNOWN_MODEL; + char path[512]; + + sprintf(path, fvendorid1, idx); + if (ReadSysFile(path, &vendor_id) < 0) { + sprintf(path, fvendorid2, idx); + ReadSysFile(path, &vendor_id); + } + sprintf(path, fdeviceid1, idx); + if (ReadSysFile(path, &device_id) < 0) { + sprintf(path, fdeviceid2, idx); + ReadSysFile(path, &device_id); + } + if (vendor_id == MICRON_VENDOR_ID) { + switch (device_id) { + case 0x5196: + fallthrough; + case 0x51A0: + fallthrough; + case 0x51A1: + fallthrough; + case 0x51A2: + eModel = M51AX; + break; + case 0x51B0: + fallthrough; + case 0x51B1: + fallthrough; + case 0x51B2: + eModel = M51BX; + break; + case 0x51C0: + fallthrough; + case 0x51C1: + fallthrough; + case 0x51C2: + fallthrough; + case 0x51C3: + eModel = M51CX; + break; + case 0x5405: + fallthrough; + case 0x5406: + fallthrough; + case 0x5407: + eModel = M5407; + break; + case 0x5410: + eModel = M5410; + break; + case 0x5411: + eModel = M5411; + break; + default: + break; + } + } + return eModel; } static int ZipAndRemoveDir(char *strDirName, char *strFileName) { - int err = 0; - char strBuffer[PATH_MAX]; - int nRet; - bool is_tgz = false; - struct stat sb; - - if (strstr(strFileName, ".tar.gz") || strstr(strFileName, ".tgz")) { - sprintf(strBuffer, "tar -zcf \"%s\" \"%s\"", strFileName, - strDirName); - is_tgz = true; - } else { - sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, - strDirName); - } - - err = EINVAL; - nRet = system(strBuffer); - - /* check if log file is created, if not print error message */ - if (nRet < 0 || (stat(strFileName, &sb) == -1)) { - if (is_tgz) - sprintf(strBuffer, "check if tar and gzip commands are installed"); - else - sprintf(strBuffer, "check if zip command is installed"); - - fprintf(stderr, "Failed to create log data package, %s!\n", strBuffer); - } - - sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); - nRet = system(strBuffer); - if (nRet < 0) - printf("Failed to remove temporary files!\n"); - - err = system("rm -f temp.txt"); - return err; + int err = 0; + char strBuffer[PATH_MAX]; + int nRet; + bool is_tgz = false; + struct stat sb; + + if (strstr(strFileName, ".tar.gz") || strstr(strFileName, ".tgz")) { + sprintf(strBuffer, "tar -zcf \"%s\" \"%s\"", strFileName, strDirName); + is_tgz = true; + } else { + sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, + strDirName); + } + + err = EINVAL; + nRet = system(strBuffer); + + /* check if log file is created, if not print error message */ + if (nRet < 0 || (stat(strFileName, &sb) == -1)) { + if (is_tgz) + sprintf(strBuffer, "check if tar and gzip commands are installed"); + else + sprintf(strBuffer, "check if zip command is installed"); + + fprintf(stderr, "Failed to create log data package, %s!\n", strBuffer); + } + + sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); + nRet = system(strBuffer); + if (nRet < 0) + printf("Failed to remove temporary files!\n"); + + err = system("rm -f temp.txt"); + return err; } static int SetupDebugDataDirectories(char *strSN, char *strFilePath, - char *strMainDirName, char *strOSDirName, - char *strCtrlDirName) + char *strMainDirName, char *strOSDirName, + char *strCtrlDirName) { - int err = 0; - char strAppend[250]; - struct stat st; - char *fileLocation = NULL; - char *fileName; - int length = 0; - int nIndex = 0; - char *strTemp = NULL; - struct stat dirStat; - int j; - int k = 0; - int i = 0; - - if (strchr(strFilePath, '/') != NULL) { - fileName = strrchr(strFilePath, '\\'); - if (fileName == NULL) { - fileName = strrchr(strFilePath, '/'); - } - - if (fileName != NULL) { - if (!strcmp(fileName, "/")) { - goto exit_status; - } - - while (strFilePath[nIndex] != '\0') { - if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) { - goto exit_status; - } - nIndex++; - } - - length = (int)strlen(strFilePath) - (int)strlen(fileName); - - if (fileName == strFilePath) { - length = 1; - } - - if ((fileLocation = (char *)malloc(length + 1)) == NULL) { - goto exit_status; - } - strncpy(fileLocation, strFilePath, length); - fileLocation[length] = '\0'; - - while (fileLocation[k] != '\0') { - if (fileLocation[k] == '\\') { - fileLocation[k] = '/'; - } - k++; - } - - length = (int)strlen(fileLocation); - - if (':' == fileLocation[length - 1]) { - if ((strTemp = (char *)malloc(length + 2)) == NULL) { - free(fileLocation); - goto exit_status; - } - strcpy(strTemp, fileLocation); - strcat(strTemp, "/"); - free(fileLocation); - - length = (int)strlen(strTemp); - if ((fileLocation = (char *)malloc(length + 1)) == NULL) { - free(strTemp); - goto exit_status; - } - - memcpy(fileLocation, strTemp, length + 1); - free(strTemp); - } - - if (stat(fileLocation, &st) != 0) { - free(fileLocation); - goto exit_status; - } - free(fileLocation); - } else { - goto exit_status; - } - } - - nIndex = 0; - for (i = 0; i < (int)strlen(strSN); i++) { - if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') { - strMainDirName[nIndex++] = strSN[i]; - } - } - strMainDirName[nIndex] = '\0'; - - j = 1; - while (stat(strMainDirName, &dirStat) == 0) { - strMainDirName[nIndex] = '\0'; - sprintf(strAppend, "-%d", j); - strcat(strMainDirName, strAppend); - j++; - } - - if (mkdir(strMainDirName, 0777) < 0) { - err = -1; - goto exit_status; - } - - if (strOSDirName != NULL) { - sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); - if (mkdir(strOSDirName, 0777) < 0) { - rmdir(strMainDirName); - err = -1; - goto exit_status; - } - } - if (strCtrlDirName != NULL) { - sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); - if (mkdir(strCtrlDirName, 0777) < 0) { - if (strOSDirName != NULL) - rmdir(strOSDirName); - rmdir(strMainDirName); - err = -1; - } - } + int err = 0; + char strAppend[250]; + struct stat st; + char *fileLocation = NULL; + char *fileName; + int length = 0; + int nIndex = 0; + char *strTemp = NULL; + struct stat dirStat; + int j; + int k = 0; + int i = 0; + + if (strchr(strFilePath, '/')) { + fileName = strrchr(strFilePath, '\\'); + if (!fileName) + fileName = strrchr(strFilePath, '/'); + + if (fileName) { + if (!strcmp(fileName, "/")) + goto exit_status; + + while (strFilePath[nIndex] != '\0') { + if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) + goto exit_status; + nIndex++; + } + + length = (int)strlen(strFilePath) - (int)strlen(fileName); + + if (fileName == strFilePath) + length = 1; + + fileLocation = (char *)malloc(length + 1); + if (!fileLocation) + goto exit_status; + strncpy(fileLocation, strFilePath, length); + fileLocation[length] = '\0'; + + while (fileLocation[k] != '\0') { + if (fileLocation[k] == '\\') + fileLocation[k] = '/'; + k++; + } + + length = (int)strlen(fileLocation); + + if (':' == fileLocation[length - 1]) { + strTemp = (char *)malloc(length + 2); + if (!strTemp) { + free(fileLocation); + goto exit_status; + } + strcpy(strTemp, fileLocation); + strcat(strTemp, "/"); + free(fileLocation); + + length = (int)strlen(strTemp); + fileLocation = (char *)malloc(length + 1); + if (!fileLocation) { + free(strTemp); + goto exit_status; + } + + memcpy(fileLocation, strTemp, length + 1); + free(strTemp); + } + + if (stat(fileLocation, &st)) { + free(fileLocation); + goto exit_status; + } + free(fileLocation); + } else { + goto exit_status; + } + } + + nIndex = 0; + for (i = 0; i < (int)strlen(strSN); i++) { + if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') + strMainDirName[nIndex++] = strSN[i]; + } + strMainDirName[nIndex] = '\0'; + + j = 1; + while (!stat(strMainDirName, &dirStat)) { + strMainDirName[nIndex] = '\0'; + sprintf(strAppend, "-%d", j); + strcat(strMainDirName, strAppend); + j++; + } + + if (mkdir(strMainDirName, 0777) < 0) { + err = -1; + goto exit_status; + } + + if (strOSDirName) { + sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); + if (mkdir(strOSDirName, 0777) < 0) { + rmdir(strMainDirName); + err = -1; + goto exit_status; + } + } + if (strCtrlDirName) { + sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); + if (mkdir(strCtrlDirName, 0777) < 0) { + if (strOSDirName) + rmdir(strOSDirName); + rmdir(strMainDirName); + err = -1; + } + } exit_status: - return err; + return err; } static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) { - int err = 0; - unsigned char pTmpBuf[CommonChunkSize] = { 0 }; - LogPageHeader_t *pLogHeader = NULL; - - if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { - err = nvme_get_log_simple(nFD, ucLogID, - CommonChunkSize, pTmpBuf); - if (err == 0) { - pLogHeader = (LogPageHeader_t *) pTmpBuf; - LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; - *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; - if (pLogHeader1->logPageHeaderFormatVersion == 0) { - printf ("Unsupported log page format version %d of log page : 0x%X\n", - ucLogID, err); - *nLogSize = 0; - err = -1; - } - } else { - printf ("Getting size of log page : 0x%X failed with %d (ignored)!\n", - ucLogID, err); - *nLogSize = 0; - } - } - return err; + int err = 0; + unsigned char pTmpBuf[CommonChunkSize] = { 0 }; + struct LogPageHeader_t *pLogHeader = NULL; + + if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { + err = nvme_get_log_simple(nFD, ucLogID, CommonChunkSize, pTmpBuf); + if (!err) { + pLogHeader = (struct LogPageHeader_t *) pTmpBuf; + struct LogPageHeader_t *pLogHeader1 = (struct LogPageHeader_t *) pLogHeader; + *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; + if (!pLogHeader1->logPageHeaderFormatVersion) { + printf("Unsupported log page format version %d of log page : 0x%X\n", + ucLogID, err); + *nLogSize = 0; + err = -1; + } + } else { + printf("Getting size of log page : 0x%X failed with %d (ignored)!\n", + ucLogID, err); + *nLogSize = 0; + } + } + return err; } static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize) { - int err = 0; - struct nvme_passthru_cmd cmd = { 0 }; - unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); - unsigned int uiMaxChunk = uiNumDwords; - unsigned int uiNumChunks = 1; - unsigned int uiXferDwords = 0; - unsigned long long ullBytesRead = 0; - unsigned char *pTempPtr = pBuffer; - unsigned char ucOpCode = 0x02; - - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - uiMaxChunk = 4096; - } else if (uiMaxChunk > 16 * 1024) { - uiMaxChunk = 16 * 1024; - } - - uiNumChunks = uiNumDwords / uiMaxChunk; - if (uiNumDwords % uiMaxChunk > 0) { - uiNumChunks += 1; - } - - for (unsigned int i = 0; i < uiNumChunks; i++) { - memset(&cmd, 0, sizeof(cmd)); - uiXferDwords = uiMaxChunk; - if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) { - uiXferDwords = uiNumDwords % uiMaxChunk; - } - - cmd.opcode = ucOpCode; - cmd.cdw10 |= ucLogID; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - - if (ucLogID == 0x7) { - cmd.cdw10 |= 0x80; - } - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - cmd.cdw11 = 1; - } - if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { - unsigned long long ullOffset = ullBytesRead; - cmd.cdw12 = ullOffset & 0xFFFFFFFF; - cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; - } - - cmd.addr = (__u64) (uintptr_t) pTempPtr; - cmd.nsid = 0xFFFFFFFF; - cmd.data_len = uiXferDwords * 4; - err = nvme_submit_admin_passthru(nFD, &cmd, NULL); - ullBytesRead += uiXferDwords * 4; - pTempPtr = pBuffer + ullBytesRead; - } - - return err; + int err = 0; + struct nvme_passthru_cmd cmd = { 0 }; + unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); + unsigned int uiMaxChunk = uiNumDwords; + unsigned int uiNumChunks = 1; + unsigned int uiXferDwords = 0; + unsigned long long ullBytesRead = 0; + unsigned char *pTempPtr = pBuffer; + unsigned char ucOpCode = 0x02; + + if (!ullBytesRead && (ucLogID == 0xE6 || ucLogID == 0xE7)) + uiMaxChunk = 4096; + else if (uiMaxChunk > 16 * 1024) + uiMaxChunk = 16 * 1024; + + uiNumChunks = uiNumDwords / uiMaxChunk; + if (uiNumDwords % uiMaxChunk > 0) + uiNumChunks += 1; + + for (unsigned int i = 0; i < uiNumChunks; i++) { + memset(&cmd, 0, sizeof(cmd)); + uiXferDwords = uiMaxChunk; + if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) + uiXferDwords = uiNumDwords % uiMaxChunk; + + cmd.opcode = ucOpCode; + cmd.cdw10 |= ucLogID; + cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; + + if (ucLogID == 0x7) + cmd.cdw10 |= 0x80; + if (!ullBytesRead && (ucLogID == 0xE6 || ucLogID == 0xE7)) + cmd.cdw11 = 1; + if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { + unsigned long long ullOffset = ullBytesRead; + + cmd.cdw12 = ullOffset & 0xFFFFFFFF; + cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; + } + + cmd.addr = (__u64) (uintptr_t) pTempPtr; + cmd.nsid = 0xFFFFFFFF; + cmd.data_len = uiXferDwords * 4; + err = nvme_submit_admin_passthru(nFD, &cmd, NULL); + ullBytesRead += uiXferDwords * 4; + pTempPtr = pBuffer + ullBytesRead; + } + + return err; } static int NVMEResetLog(int nFD, unsigned char ucLogID, int nBufferSize, - long long llMaxSize) + long long llMaxSize) { - unsigned int *pBuffer = NULL; - int err = 0; + unsigned int *pBuffer = NULL; + int err = 0; - if ((pBuffer = (unsigned int *)calloc(1, nBufferSize)) == NULL) - return err; + pBuffer = (unsigned int *)calloc(1, nBufferSize); + if (!pBuffer) + return err; - while (err == 0 && llMaxSize > 0) { - err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); - if (err) { - free(pBuffer); - return err; - } + while (!err && llMaxSize > 0) { + err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); + if (err) { + free(pBuffer); + return err; + } - if (pBuffer[0] == 0xdeadbeef) - break; + if (pBuffer[0] == 0xdeadbeef) + break; - llMaxSize = llMaxSize - nBufferSize; - } + llMaxSize = llMaxSize - nBufferSize; + } - free(pBuffer); - return err; + free(pBuffer); + return err; } static int GetCommonLogPage(int nFD, unsigned char ucLogID, - unsigned char **pBuffer, int nBuffSize) + unsigned char **pBuffer, int nBuffSize) { - unsigned char *pTempPtr = NULL; - int err = 0; - pTempPtr = (unsigned char *)malloc(nBuffSize); - if (!pTempPtr) { - goto exit_status; - } - memset(pTempPtr, 0, nBuffSize); - err = nvme_get_log_simple(nFD, ucLogID, nBuffSize, pTempPtr); - *pBuffer = pTempPtr; + unsigned char *pTempPtr = NULL; + int err = 0; + + pTempPtr = (unsigned char *)malloc(nBuffSize); + if (!pTempPtr) + goto exit_status; + memset(pTempPtr, 0, nBuffSize); + err = nvme_get_log_simple(nFD, ucLogID, nBuffSize, pTempPtr); + *pBuffer = pTempPtr; exit_status: - return err; + return err; } /* @@ -456,1531 +467,1526 @@ exit_status: static int micron_parse_options(struct nvme_dev **dev, int argc, char **argv, const char *desc, struct argconfig_commandline_options *opts, - eDriveModel *modelp) + enum eDriveModel *modelp) { - int idx = 0; - int err = parse_and_open(dev, argc, argv, desc, opts); + int idx; + int err = parse_and_open(dev, argc, argv, desc, opts); - if (err) { - perror("open"); - return -1; - } + if (err) { + perror("open"); + return -1; + } - if (modelp) { - sscanf(argv[optind], "/dev/nvme%d", &idx); - *modelp = GetDriveModel(idx); - } + if (modelp) { + if (sscanf(argv[optind], "/dev/nvme%d", &idx) != 1) + idx = 0; + *modelp = GetDriveModel(idx); + } - return 0; + return 0; } static int micron_fw_commit(int fd, int select) { - struct nvme_passthru_cmd cmd = { - .opcode = nvme_admin_fw_commit, - .cdw10 = 8, - .cdw12 = select, - }; - return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); + struct nvme_passthru_cmd cmd = { + .opcode = nvme_admin_fw_commit, + .cdw10 = 8, + .cdw12 = select, + }; + return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); } static int micron_selective_download(int argc, char **argv, - struct command *cmd, struct plugin *plugin) + struct command *cmd, struct plugin *plugin) { - const char *desc = - "This performs a selective firmware download, which allows the user to " - "select which firmware binary to update for 9200 devices. This requires " - "a power cycle once the update completes. The options available are: \n\n" - "OOB - This updates the OOB and main firmware\n" - "EEP - This updates the eeprom and main firmware\n" - "ALL - This updates the eeprom, OOB, and main firmware"; - const char *fw = "firmware file (required)"; - const char *select = "FW Select (e.g., --select=ALL)"; - int xfer = 4096; - void *fw_buf; - int selectNo, fw_fd, fw_size, err, offset = 0; - struct nvme_dev *dev; - struct stat sb; - - struct config { - char *fw; - char *select; - }; - - struct config cfg = { - .fw = "", - .select = "\0", - }; - - OPT_ARGS(opts) = { - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_STRING("select", 's', "flag", &cfg.select, select), - OPT_END() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) - return err; - - if (strlen(cfg.select) != 3) { - fprintf(stderr, "Invalid select flag\n"); - dev_close(dev); - return EINVAL; - } - - for (int i = 0; i < 3; i++) { - cfg.select[i] = toupper(cfg.select[i]); - } - - if (strncmp(cfg.select, "OOB", 3) == 0) { - selectNo = 18; - } else if (strncmp(cfg.select, "EEP", 3) == 0) { - selectNo = 10; - } else if (strncmp(cfg.select, "ALL", 3) == 0) { - selectNo = 26; - } else { - fprintf(stderr, "Invalid select flag\n"); - dev_close(dev); - return EINVAL; - } - - fw_fd = open(cfg.fw, O_RDONLY); - if (fw_fd < 0) { - fprintf(stderr, "no firmware file provided\n"); - dev_close(dev); - return EINVAL; - } - - err = fstat(fw_fd, &sb); - if (err < 0) { - perror("fstat"); - err = errno; - goto out; - } - - fw_size = sb.st_size; - if (fw_size & 0x3) { - fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); - err = EINVAL; - goto out; - } - - if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { - fprintf(stderr, "No memory for f/w size:%d\n", fw_size); - err = ENOMEM; - goto out; - } - - if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) { - err = errno; - goto out_free; - } - - while (fw_size > 0) { - xfer = min(xfer, fw_size); - - struct nvme_fw_download_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .offset = offset, - .data_len = xfer, - .data = fw_buf, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = NULL, + const char *desc = + "This performs a selective firmware download, which allows the user to\n" + "select which firmware binary to update for 9200 devices. This requires\n" + "a power cycle once the update completes. The options available are:\n\n" + "OOB - This updates the OOB and main firmware\n" + "EEP - This updates the eeprom and main firmware\n" + "ALL - This updates the eeprom, OOB, and main firmware"; + const char *fw = "firmware file (required)"; + const char *select = "FW Select (e.g., --select=ALL)"; + int xfer = 4096; + void *fw_buf; + int selectNo, fw_fd, fw_size, err, offset = 0; + struct nvme_dev *dev; + struct stat sb; + + struct config { + char *fw; + char *select; + }; + + struct config cfg = { + .fw = "", + .select = "\0", + }; + + OPT_ARGS(opts) = { + OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), + OPT_STRING("select", 's', "flag", &cfg.select, select), + OPT_END() }; - err = nvme_fw_download(&args); - if (err < 0) { - perror("fw-download"); - goto out_free; - } else if (err != 0) { - nvme_show_status(err); - goto out_free; - } - fw_buf += xfer; - fw_size -= xfer; - offset += xfer; - } - - err = micron_fw_commit(dev_fd(dev), selectNo); - - if (err == 0x10B || err == 0x20B) { - err = 0; - fprintf(stderr, - "Update successful! Power cycle for changes to take effect\n"); - } + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + if (strlen(cfg.select) != 3) { + fprintf(stderr, "Invalid select flag\n"); + dev_close(dev); + return -EINVAL; + } + + for (int i = 0; i < 3; i++) + cfg.select[i] = toupper(cfg.select[i]); + + if (!strncmp(cfg.select, "OOB", 3)) { + selectNo = 18; + } else if (!strncmp(cfg.select, "EEP", 3)) { + selectNo = 10; + } else if (!strncmp(cfg.select, "ALL", 3)) { + selectNo = 26; + } else { + fprintf(stderr, "Invalid select flag\n"); + dev_close(dev); + return -EINVAL; + } + + fw_fd = open(cfg.fw, O_RDONLY); + if (fw_fd < 0) { + fprintf(stderr, "no firmware file provided\n"); + dev_close(dev); + return -EINVAL; + } + + err = fstat(fw_fd, &sb); + if (err < 0) { + perror("fstat"); + err = errno; + goto out; + } + + fw_size = sb.st_size; + if (fw_size & 0x3) { + fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); + err = EINVAL; + goto out; + } + + if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { + fprintf(stderr, "No memory for f/w size:%d\n", fw_size); + err = ENOMEM; + goto out; + } + + if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) { + err = errno; + goto out_free; + } + + while (fw_size > 0) { + xfer = min(xfer, fw_size); + + struct nvme_fw_download_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .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_free; + } else if (err) { + nvme_show_status(err); + goto out_free; + } + fw_buf += xfer; + fw_size -= xfer; + offset += xfer; + } + + err = micron_fw_commit(dev_fd(dev), selectNo); + + if (err == 0x10B || err == 0x20B) { + err = 0; + fprintf(stderr, + "Update successful! Power cycle for changes to take effect\n"); + } out_free: - free(fw_buf); + free(fw_buf); out: - close(fw_fd); - dev_close(dev); - return err; + close(fw_fd); + dev_close(dev); + return err; } static int micron_smbus_option(int argc, char **argv, - struct command *cmd, struct plugin *plugin) + struct command *cmd, struct plugin *plugin) { - __u32 result = 0; - __u32 cdw11 = 0; - const char *desc = "Enable/Disable/Get status of SMBUS option on controller"; - const char *option = "enable or disable or status"; - const char *value = "1 - hottest component temperature, 0 - composite " - "temperature (default) for enable option, 0 (current), " - "1 (default), 2 (saved) for status options"; - const char *save = "1 - persistent, 0 - non-persistent (default)"; - int fid = MICRON_FEATURE_SMBUS_OPTION; - eDriveModel model = UNKNOWN_MODEL; - struct nvme_dev *dev; - int err = 0; - - struct { - char *option; - int value; - int save; - int status; - } opt = { - .option = "disable", - .value = 0, - .save = 0, - .status = 0, - }; - - OPT_ARGS(opts) = { - OPT_STRING("option", 'o', "option", &opt.option, option), - OPT_UINT("value", 'v', &opt.value, value), - OPT_UINT("save", 's', &opt.save, save), - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 0) - return err; - - if (model != M5407 && model != M5411) { - printf ("This option is not supported for specified drive\n"); - dev_close(dev); - return err; - } - - if (!strcmp(opt.option, "enable")) { - cdw11 = opt.value << 1 | 1; - err = nvme_set_features_simple(dev_fd(dev), fid, 1, cdw11, opt.save, - &result); - if (err == 0) { - printf("successfully enabled SMBus on drive\n"); - } else { - printf("Failed to enabled SMBus on drive\n"); - } - } - else if (!strcmp(opt.option, "status")) { - struct nvme_get_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .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", - (result & 2) ? "hottest component" : "composite"); - } else { - printf("Failed to retrieve SMBus status on the drive\n"); - } - } - else if (!strcmp(opt.option, "disable")) { - cdw11 = opt.value << 1 | 0; - err = nvme_set_features_simple(dev_fd(dev), fid, 1, cdw11, opt.save, - &result); - if (err == 0) { - printf("Successfully disabled SMBus on drive\n"); - } else { - printf("Failed to disable SMBus on drive\n"); - } - } else { - printf("Invalid option %s, valid values are enable, disable or status\n", - opt.option); - dev_close(dev); - return -1; - } - - close(dev_fd(dev)); - return err; + __u32 result = 0; + __u32 cdw11 = 0; + const char *desc = "Enable/Disable/Get status of SMBUS option on controller"; + const char *option = "enable or disable or status"; + const char *value = + "1 - hottest component temperature, 0 - composite temperature (default) for enable option, 0 (current), 1 (default), 2 (saved) for status options"; + const char *save = "1 - persistent, 0 - non-persistent (default)"; + int fid = MICRON_FEATURE_SMBUS_OPTION; + enum eDriveModel model = UNKNOWN_MODEL; + struct nvme_dev *dev; + int err = 0; + + struct { + char *option; + int value; + int save; + int status; + } opt = { + .option = "disable", + .value = 0, + .save = 0, + .status = 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("value", 'v', &opt.value, value), + OPT_UINT("save", 's', &opt.save, save), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 0) + return err; + + if (model != M5407 && model != M5411) { + printf("This option is not supported for specified drive\n"); + dev_close(dev); + return err; + } + + if (!strcmp(opt.option, "enable")) { + cdw11 = opt.value << 1 | 1; + err = nvme_set_features_simple(dev_fd(dev), fid, 1, cdw11, opt.save, + &result); + if (!err) + printf("successfully enabled SMBus on drive\n"); + else + printf("Failed to enabled SMBus on drive\n"); + } else if (!strcmp(opt.option, "status")) { + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .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) + printf("SMBus status on the drive: %s (returns %s temperature)\n", + (result & 1) ? "enabled" : "disabled", + (result & 2) ? "hottest component" : "composite"); + else + printf("Failed to retrieve SMBus status on the drive\n"); + } else if (!strcmp(opt.option, "disable")) { + cdw11 = opt.value << 1 | 0; + err = nvme_set_features_simple(dev_fd(dev), fid, 1, cdw11, opt.save, + &result); + if (!err) + printf("Successfully disabled SMBus on drive\n"); + else + printf("Failed to disable SMBus on drive\n"); + } else { + printf("Invalid option %s, valid values are enable, disable or status\n", + opt.option); + dev_close(dev); + return -1; + } + + close(dev_fd(dev)); + return err; } static int micron_temp_stats(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - struct nvme_smart_log smart_log; - unsigned int temperature = 0, i = 0, err = 0; - unsigned int tempSensors[SensorCount] = { 0 }; - const char *desc = "Retrieve Micron temperature info for the given device "; - const char *fmt = "output format normal|json"; - struct format { - char *fmt; - }; - struct format cfg = { - .fmt = "normal", - }; - bool is_json = false; - struct json_object *root; - struct json_object *logPages; - struct nvme_dev *dev; - - OPT_ARGS(opts) = { - OPT_FMT("format", 'f', &cfg.fmt, fmt), - OPT_END() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) { - printf("\nDevice not found \n");; - return -1; - } - - if (strcmp(cfg.fmt, "json") == 0) - is_json = true; - - err = nvme_get_log_smart(dev_fd(dev), 0xffffffff, false, &smart_log); - if (!err) { - temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); - temperature = temperature ? temperature - 273 : 0; - for (i = 0; i < SensorCount && tempSensors[i] != 0; i++) { - tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); - tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; - } - if (is_json) { - struct json_object *stats = json_create_object(); - char tempstr[64] = { 0 }; - root = json_create_object(); - logPages = json_create_array(); - json_object_add_value_array(root, "Micron temperature information", logPages); - sprintf(tempstr, "%u C", temperature); - json_object_add_value_string(stats, "Current Composite Temperature", tempstr); - for (i = 0; i < SensorCount && tempSensors[i] != 0; i++) { - char sensor_str[256] = { 0 }; - char datastr[64] = { 0 }; - sprintf(sensor_str, "Temperature Sensor #%d", (i + 1)); - sprintf(datastr, "%u C", tempSensors[i]); - json_object_add_value_string(stats, sensor_str, datastr); - } - json_array_add_value_object(logPages, stats); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } else { - printf("Micron temperature information:\n"); - printf("%-10s : %u C\n", "Current Composite Temperature", temperature); - for (i = 0; i < SensorCount && tempSensors[i] != 0; i++) { - printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); - } - } - } - dev_close(dev); - return err; + struct nvme_smart_log smart_log; + unsigned int temperature = 0, i = 0, err = 0; + unsigned int tempSensors[SensorCount] = { 0 }; + const char *desc = "Retrieve Micron temperature info for the given device "; + const char *fmt = "output format normal|json"; + struct format { + char *fmt; + }; + struct format cfg = { + .fmt = "normal", + }; + bool is_json = false; + struct json_object *root; + struct json_object *logPages; + struct nvme_dev *dev; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("\nDevice not found\n"); + return -1; + } + + if (!strcmp(cfg.fmt, "json")) + is_json = true; + + err = nvme_get_log_smart(dev_fd(dev), 0xffffffff, false, &smart_log); + if (!err) { + temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); + temperature = temperature ? temperature - 273 : 0; + for (i = 0; i < SensorCount && tempSensors[i]; i++) { + tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); + tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; + } + if (is_json) { + struct json_object *stats = json_create_object(); + char tempstr[64] = { 0 }; + + root = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Micron temperature information", logPages); + sprintf(tempstr, "%u C", temperature); + json_object_add_value_string(stats, "Current Composite Temperature", tempstr); + for (i = 0; i < SensorCount && tempSensors[i]; i++) { + char sensor_str[256] = { 0 }; + char datastr[64] = { 0 }; + + sprintf(sensor_str, "Temperature Sensor #%d", (i + 1)); + sprintf(datastr, "%u C", tempSensors[i]); + json_object_add_value_string(stats, sensor_str, datastr); + } + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + printf("Micron temperature information:\n"); + printf("%-10s : %u C\n", "Current Composite Temperature", temperature); + for (i = 0; i < SensorCount && tempSensors[i]; i++) + printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); + } + } + dev_close(dev); + return err; } static int micron_pcie_stats(int argc, char **argv, - struct command *cmd, struct plugin *plugin) + struct command *cmd, struct plugin *plugin) { - int i, err = 0, bus = 0, domain = 0, device = 0, function = 0, ctrlIdx; - char strTempFile[1024], strTempFile2[1024], command[1024]; - struct nvme_dev *dev; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[NAME_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char uncorrectable[8] = { 0 }; - struct nvme_passthru_cmd admin_cmd = { 0 }; - eDriveModel eModel = UNKNOWN_MODEL; - char *res; - bool is_json = true; - bool counters = false; - struct format { - char *fmt; - }; - const char *desc = "Retrieve PCIe event counters"; - const char *fmt = "output format json|normal"; - struct format cfg = { - .fmt = "json", - }; - struct pcie_error_counters { - __u16 receiver_error; - __u16 bad_tlp; - __u16 bad_dllp; - __u16 replay_num_rollover; - __u16 replay_timer_timeout; - __u16 advisory_non_fatal_error; - __u16 DLPES; - __u16 poisoned_tlp; - __u16 FCPC; - __u16 completion_timeout; - __u16 completion_abort; - __u16 unexpected_completion; - __u16 receiver_overflow; - __u16 malformed_tlp; - __u16 ecrc_error; - __u16 unsupported_request_error; - } pcie_error_counters = { 0 }; - - struct { - char *err; - int bit; - int val; - } pcie_correctable_errors[] = { - { "Unsupported Request Error Status (URES)", 20, + int i, err = 0, bus, domain, device, function, ctrlIdx; + char strTempFile[1024], strTempFile2[1024], command[1024]; + struct nvme_dev *dev; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[NAME_MAX] = { 0 }; + ssize_t sLinkSize = 0; + FILE *fp; + char correctable[8] = { 0 }; + char uncorrectable[8] = { 0 }; + struct nvme_passthru_cmd admin_cmd = { 0 }; + enum eDriveModel eModel = UNKNOWN_MODEL; + char *res; + bool is_json = true; + bool counters = false; + struct format { + char *fmt; + }; + const char *desc = "Retrieve PCIe event counters"; + const char *fmt = "output format json|normal"; + struct format cfg = { + .fmt = "json", + }; + struct pcie_error_counters { + __u16 receiver_error; + __u16 bad_tlp; + __u16 bad_dllp; + __u16 replay_num_rollover; + __u16 replay_timer_timeout; + __u16 advisory_non_fatal_error; + __u16 DLPES; + __u16 poisoned_tlp; + __u16 FCPC; + __u16 completion_timeout; + __u16 completion_abort; + __u16 unexpected_completion; + __u16 receiver_overflow; + __u16 malformed_tlp; + __u16 ecrc_error; + __u16 unsupported_request_error; + } pcie_error_counters = { 0 }; + + struct { + char *err; + int bit; + int val; + } pcie_correctable_errors[] = { + { "Unsupported Request Error Status (URES)", 20, offsetof(struct pcie_error_counters, unsupported_request_error)}, - { "ECRC Error Status (ECRCES)", 19, + { "ECRC Error Status (ECRCES)", 19, offsetof(struct pcie_error_counters, ecrc_error)}, - { "Malformed TLP Status (MTS)", 18, + { "Malformed TLP Status (MTS)", 18, offsetof(struct pcie_error_counters, malformed_tlp)}, - { "Receiver Overflow Status (ROS)", 17, + { "Receiver Overflow Status (ROS)", 17, offsetof(struct pcie_error_counters, receiver_overflow)}, - { "Unexpected Completion Status (UCS)", 16, + { "Unexpected Completion Status (UCS)", 16, offsetof(struct pcie_error_counters, unexpected_completion)}, - { "Completer Abort Status (CAS)", 15, + { "Completer Abort Status (CAS)", 15, offsetof(struct pcie_error_counters, completion_abort)}, - { "Completion Timeout Status (CTS)", 14, + { "Completion Timeout Status (CTS)", 14, offsetof(struct pcie_error_counters, completion_timeout)}, - { "Flow Control Protocol Error Status (FCPES)", 13, + { "Flow Control Protocol Error Status (FCPES)", 13, offsetof(struct pcie_error_counters, FCPC)}, - { "Poisoned TLP Status (PTS)", 12, + { "Poisoned TLP Status (PTS)", 12, offsetof(struct pcie_error_counters, poisoned_tlp)}, - { "Data Link Protocol Error Status (DLPES)", 4, + { "Data Link Protocol Error Status (DLPES)", 4, offsetof(struct pcie_error_counters, DLPES)}, - }, - pcie_uncorrectable_errors[] = { - { "Advisory Non-Fatal Error Status (ANFES)", 13, + }, + pcie_uncorrectable_errors[] = { + { "Advisory Non-Fatal Error Status (ANFES)", 13, offsetof(struct pcie_error_counters, advisory_non_fatal_error)}, - { "Replay Timer Timeout Status (RTS)", 12, + { "Replay Timer Timeout Status (RTS)", 12, offsetof(struct pcie_error_counters, replay_timer_timeout)}, - { "REPLAY_NUM Rollover Status (RRS)", 8, + { "REPLAY_NUM Rollover Status (RRS)", 8, offsetof(struct pcie_error_counters, replay_num_rollover)}, - { "Bad DLLP Status (BDS)", 7, + { "Bad DLLP Status (BDS)", 7, offsetof(struct pcie_error_counters, bad_dllp)}, - { "Bad TLP Status (BTS)", 6, + { "Bad TLP Status (BTS)", 6, offsetof(struct pcie_error_counters, bad_tlp)}, - { "Receiver Error Status (RES)", 0, + { "Receiver Error Status (RES)", 0, offsetof(struct pcie_error_counters, receiver_error)}, - }; - - __u32 correctable_errors; - __u32 uncorrectable_errors; - - OPT_ARGS(opts) = { - OPT_FMT("format", 'f', &cfg.fmt, fmt), - OPT_END() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) { - printf("\nDevice not found \n");; - return -1; - } - - /* pull log details based on the model name */ - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { - printf ("Unsupported drive model for vs-pcie-stats command\n"); - goto out; - } - - if (strcmp(cfg.fmt, "normal") == 0) - is_json = false; - - if (eModel == M5407) { - admin_cmd.opcode = 0xD6; - admin_cmd.addr = (__u64)(uintptr_t)&pcie_error_counters; - admin_cmd.data_len = sizeof(pcie_error_counters); - admin_cmd.cdw10 = 1; - err = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL); - if (!err) { - counters = true; - correctable_errors = 10; - uncorrectable_errors = 6; - goto print_stats; - } - } - - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - sprintf(strTempFile, "/sys/block/%s/device", devicename); - memset(strTempFile2, 0x0, 1024); - sLinkSize = readlink(strTempFile, strTempFile2, 1023); - if (sLinkSize < 0) { - err = -errno; - printf("Failed to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - sprintf(strTempFile, "/sys/block/%s/device/device", devicename); - memset(strTempFile2, 0x0, 1024); - sLinkSize = readlink(strTempFile, strTempFile2, 1023); - if (sLinkSize < 0) { - err = -errno; - printf("Failed to read device\n"); - goto out; - } - } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Failed to retrieve error count\n"); - goto out; - } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Failed to retrieve error count\n"); - pclose(fp); - goto out; - } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Failed to retrieve error count\n"); - goto out; - } - res = fgets(uncorrectable, sizeof(uncorrectable), fp); - if (res == NULL) { - printf("Failed to retrieve error count\n"); - pclose(fp); - goto out; - } - pclose(fp); - - correctable_errors = (__u32)strtol(correctable, NULL, 16); - uncorrectable_errors = (__u32)strtol(uncorrectable, NULL, 16); + }; + + __u32 correctable_errors; + __u32 uncorrectable_errors; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("\nDevice not found\n"); + return -1; + } + + /* pull log details based on the model name */ + if (sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx) != 1) + ctrlIdx = 0; + eModel = GetDriveModel(ctrlIdx); + if (eModel == UNKNOWN_MODEL) { + printf("Unsupported drive model for vs-pcie-stats command\n"); + goto out; + } + + if (!strcmp(cfg.fmt, "normal")) + is_json = false; + + if (eModel == M5407) { + admin_cmd.opcode = 0xD6; + admin_cmd.addr = (__u64)(uintptr_t)&pcie_error_counters; + admin_cmd.data_len = sizeof(pcie_error_counters); + admin_cmd.cdw10 = 1; + err = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL); + if (!err) { + counters = true; + correctable_errors = 10; + uncorrectable_errors = 6; + goto print_stats; + } + } + + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + sprintf(strTempFile, "/sys/block/%s/device", devicename); + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + sprintf(strTempFile, "/sys/block/%s/device/device", devicename); + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + if (sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function) != 4) + domain = bus = device = function = 0; + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, + function); + fp = popen(command, "r"); + if (!fp) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (!res) { + printf("Failed to retrieve error count\n"); + pclose(fp); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, + function); + fp = popen(command, "r"); + if (!fp) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(uncorrectable, sizeof(uncorrectable), fp); + if (!res) { + printf("Failed to retrieve error count\n"); + pclose(fp); + goto out; + } + pclose(fp); + + correctable_errors = (__u32)strtol(correctable, NULL, 16); + uncorrectable_errors = (__u32)strtol(uncorrectable, NULL, 16); print_stats: - if (is_json) { - - struct json_object *root = json_create_object(); - struct json_object *pcieErrors = json_create_array(); - struct json_object *stats = json_create_object(); - __u8 *pcounter = (__u8 *)&pcie_error_counters; - - json_object_add_value_array(root, "PCIE Stats", pcieErrors); - for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { - __u16 val = counters ? *(__u16 *)(pcounter + pcie_correctable_errors[i].val) : - (correctable_errors >> pcie_correctable_errors[i].bit) & 1; - json_object_add_value_int(stats, pcie_correctable_errors[i].err, val); - } - for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { - __u16 val = counters ? *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val) : - (uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1; - json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, val); - } - json_array_add_value_object(pcieErrors, stats); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } else if (counters == true) { - __u8 *pcounter = (__u8 *)&pcie_error_counters; - for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { - printf("%-42s : %-1hu\n", pcie_correctable_errors[i].err, - *(__u16 *)(pcounter + pcie_correctable_errors[i].val)); - } - for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { - printf("%-42s : %-1hu\n", pcie_uncorrectable_errors[i].err, - *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val)); - } - } else if (eModel == M5407 || eModel == M5410) { - for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { - printf("%-42s : %-1d\n", pcie_correctable_errors[i].err, - ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); - } - for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { - printf("%-42s : %-1d\n", pcie_uncorrectable_errors[i].err, - ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); - } - } else { - printf("PCIE Stats:\n"); - printf("Device correctable errors detected: %s\n", correctable); - printf("Device uncorrectable errors detected: %s\n", uncorrectable); - } + if (is_json) { + struct json_object *root = json_create_object(); + struct json_object *pcieErrors = json_create_array(); + struct json_object *stats = json_create_object(); + __u8 *pcounter = (__u8 *)&pcie_error_counters; + + json_object_add_value_array(root, "PCIE Stats", pcieErrors); + for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors); i++) { + __u16 val = counters ? *(__u16 *)(pcounter + pcie_correctable_errors[i].val) : + (correctable_errors >> pcie_correctable_errors[i].bit) & 1; + json_object_add_value_int(stats, pcie_correctable_errors[i].err, val); + } + for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors); i++) { + __u16 val = counters ? *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val) : + (uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1; + json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, val); + } + json_array_add_value_object(pcieErrors, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else if (counters == true) { + __u8 *pcounter = (__u8 *)&pcie_error_counters; + + for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors); i++) + printf("%-42s : %-1hu\n", pcie_correctable_errors[i].err, + *(__u16 *)(pcounter + pcie_correctable_errors[i].val)); + for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors); i++) + printf("%-42s : %-1hu\n", pcie_uncorrectable_errors[i].err, + *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val)); + } else if (eModel == M5407 || eModel == M5410) { + for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors); i++) + printf("%-42s : %-1d\n", pcie_correctable_errors[i].err, + ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); + for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors); i++) + printf("%-42s : %-1d\n", pcie_uncorrectable_errors[i].err, + ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); + } else { + printf("PCIE Stats:\n"); + printf("Device correctable errors detected: %s\n", correctable); + printf("Device uncorrectable errors detected: %s\n", uncorrectable); + } out: - dev_close(dev); - return err; + dev_close(dev); + return err; } static int micron_clear_pcie_correctable_errors(int argc, char **argv, - struct command *cmd, - struct plugin *plugin) + struct command *cmd, + struct plugin *plugin) { - int err = -EINVAL, bus = 0, domain = 0, device = 0, function = 0; - char strTempFile[1024], strTempFile2[1024], command[1024]; - struct nvme_dev *dev; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[PATH_MAX] = { 0 }; - ssize_t sLinkSize = 0; - eDriveModel model = UNKNOWN_MODEL; - struct nvme_passthru_cmd admin_cmd = { 0 }; - char correctable[8] = { 0 }; - FILE *fp; - char *res; - const char *desc = "Clear PCIe Device Correctable Errors"; - __u32 result = 0; - __u8 fid = MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS; - OPT_ARGS(opts) = { - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 0) - return err; - - /* For M51CX models, PCIe errors are cleared using 0xC3 feature */ - if (model == M51CX) { - err = nvme_set_features_simple(dev_fd(dev), fid, 0, (1 << 31), false, - &result); - if (err == 0 && (err = (int)result) == 0) { - printf("Device correctable errors are cleared!\n"); - goto out; - } - } else if (model == M5407) { - admin_cmd.opcode = 0xD6; - admin_cmd.addr = 0; - admin_cmd.cdw10 = 0; - err = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL); - if (err == 0) { - printf("Device correctable error counters are cleared!\n"); - goto out; - } else { - /* proceed to clear status bits using sysfs interface - printf("Error clearing PCIe correctable errors = 0x%x\n", err); */ - } - } - - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device", devicename); - if (err < 0) - goto out; - - memset(strTempFile2, 0x0, 1024); - sLinkSize = readlink(strTempFile, strTempFile2, 1023); - if (sLinkSize < 0) { - err = -errno; - printf("Failed to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device/device", devicename); - if (err < 0) - goto out; - memset(strTempFile2, 0x0, 1024); - sLinkSize = readlink(strTempFile, strTempFile2, 1023); - if (sLinkSize < 0) { - err = -errno; - printf("Failed to read device\n"); - goto out; - } - } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, - device, function); - err = -1; - fp = popen(command, "r"); - if (fp == NULL) { - printf("Failed to clear error count\n"); - goto out; - } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Failed to retrieve error count\n"); - goto out; - } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Failed to retrieve error count\n"); - pclose(fp); - goto out; - } - pclose(fp); - printf("Device correctable errors cleared!\n"); - printf("Device correctable errors detected: %s\n", correctable); - err = 0; + int err = -EINVAL, bus, domain, device, function; + char strTempFile[1024], strTempFile2[1024], command[1024]; + struct nvme_dev *dev; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[PATH_MAX] = { 0 }; + ssize_t sLinkSize = 0; + enum eDriveModel model = UNKNOWN_MODEL; + struct nvme_passthru_cmd admin_cmd = { 0 }; + char correctable[8] = { 0 }; + FILE *fp; + char *res; + const char *desc = "Clear PCIe Device Correctable Errors"; + __u32 result = 0; + __u8 fid = MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS; + + OPT_ARGS(opts) = { + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 0) + return err; + + /* For M51CX models, PCIe errors are cleared using 0xC3 feature */ + if (model == M51CX) { + err = nvme_set_features_simple(dev_fd(dev), fid, 0, (1 << 31), false, + &result); + if (!err) + err = (int)result; + if (!err) { + printf("Device correctable errors are cleared!\n"); + goto out; + } + } else if (model == M5407) { + admin_cmd.opcode = 0xD6; + admin_cmd.addr = 0; + admin_cmd.cdw10 = 0; + err = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL); + if (!err) { + printf("Device correctable error counters are cleared!\n"); + goto out; + } else { + /* proceed to clear status bits using sysfs interface */ + } + } + + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device", devicename); + if (err < 0) + goto out; + + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device/device", devicename); + if (err < 0) + goto out; + memset(strTempFile2, 0x0, 1024); + sLinkSize = readlink(strTempFile, strTempFile2, 1023); + if (sLinkSize < 0) { + err = -errno; + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + if (sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function) != 4) + domain = bus = device = function = 0; + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, + device, function); + err = -1; + fp = popen(command, "r"); + if (!fp) { + printf("Failed to clear error count\n"); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, + function); + fp = popen(command, "r"); + if (!fp) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (!res) { + printf("Failed to retrieve error count\n"); + pclose(fp); + goto out; + } + pclose(fp); + printf("Device correctable errors cleared!\n"); + printf("Device correctable errors detected: %s\n", correctable); + err = 0; out: - dev_close(dev); - return err; + dev_close(dev); + return err; } static struct logpage { - const char *field; - char datastr[128]; + const char *field; + char datastr[128]; } d0_log_page[] = { - { "NAND Writes (Bytes Written)", { 0 }}, - { "Program Failure Count", { 0 }}, - { "Erase Failures", { 0 }}, - { "Bad Block Count", { 0 }}, - { "NAND XOR/RAID Recovery Trigger Events", { 0 }}, - { "NSZE Change Supported", { 0 }}, - { "Number of NSZE Modifications", { 0 }} + { "NAND Writes (Bytes Written)", { 0 }}, + { "Program Failure Count", { 0 }}, + { "Erase Failures", { 0 }}, + { "Bad Block Count", { 0 }}, + { "NAND XOR/RAID Recovery Trigger Events", { 0 }}, + { "NSZE Change Supported", { 0 }}, + { "Number of NSZE Modifications", { 0 }} }; static void init_d0_log_page(__u8 *buf, __u8 nsze) { - unsigned int logD0[D0_log_size/sizeof(int)] = { 0 }; - __u64 count_lo, count_hi, count; + unsigned int logD0[D0_log_size/sizeof(int)] = { 0 }; + __u64 count_lo, count_hi, count; - memcpy(logD0, buf, sizeof(logD0)); + memcpy(logD0, buf, sizeof(logD0)); - count = ((__u64)logD0[45] << 32) | logD0[44]; - sprintf(d0_log_page[0].datastr, "0x%"PRIx64, le64_to_cpu(count)); + count = ((__u64)logD0[45] << 32) | logD0[44]; + sprintf(d0_log_page[0].datastr, "0x%"PRIx64, le64_to_cpu(count)); - count_hi = ((__u64)logD0[39] << 32) | logD0[38]; - count_lo = ((__u64)logD0[37] << 32) | logD0[36]; - if (count_hi != 0) - sprintf(d0_log_page[1].datastr, "0x%"PRIx64"%016"PRIx64, - le64_to_cpu(count_hi), le64_to_cpu(count_lo)); - else - sprintf(d0_log_page[1].datastr, "0x%"PRIx64, le64_to_cpu(count_lo)); + count_hi = ((__u64)logD0[39] << 32) | logD0[38]; + count_lo = ((__u64)logD0[37] << 32) | logD0[36]; + if (count_hi) + sprintf(d0_log_page[1].datastr, "0x%"PRIx64"%016"PRIx64, + le64_to_cpu(count_hi), le64_to_cpu(count_lo)); + else + sprintf(d0_log_page[1].datastr, "0x%"PRIx64, le64_to_cpu(count_lo)); - count = ((__u64)logD0[25] << 32) | logD0[24]; - sprintf(d0_log_page[2].datastr, "0x%"PRIx64, le64_to_cpu(count)); + count = ((__u64)logD0[25] << 32) | logD0[24]; + sprintf(d0_log_page[2].datastr, "0x%"PRIx64, le64_to_cpu(count)); - sprintf(d0_log_page[3].datastr, "0x%x", logD0[3]); + sprintf(d0_log_page[3].datastr, "0x%x", logD0[3]); - count_lo = ((__u64)logD0[37] << 32) | logD0[36]; - count = ((__u64)logD0[25] << 32) | logD0[24]; - count = (__u64)logD0[3] - (count_lo + count); - sprintf(d0_log_page[4].datastr, "0x%"PRIx64, le64_to_cpu(count)); + count_lo = ((__u64)logD0[37] << 32) | logD0[36]; + count = ((__u64)logD0[25] << 32) | logD0[24]; + count = (__u64)logD0[3] - (count_lo + count); + sprintf(d0_log_page[4].datastr, "0x%"PRIx64, le64_to_cpu(count)); - sprintf(d0_log_page[5].datastr, "0x%x", nsze); - sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]); + sprintf(d0_log_page[5].datastr, "0x%x", nsze); + sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]); } /* OCP and Vendor specific log data format */ struct micron_vs_logpage { - char *field; - int size; /* FB client spec version 1.0 sizes - M5410 models */ - int size2; /* FB client spec version 0.7 sizes - M5407 models */ + char *field; + 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 M51CX models */ ocp_c0_log_page[] = { - { "Physical Media Units Written", 16}, - { "Physical Media Units Read", 16 }, - { "Raw Bad User NAND Block Count", 6}, - { "Normalized Bad User NAND Block Count", 2}, - { "Raw Bad System NAND Block Count", 6}, - { "Normalized Bad System NAND Block Count", 2}, - { "XOR Recovery Count", 8}, - { "Uncorrectable Read Error Count", 8}, - { "Soft ECC Error Count", 8}, - { "SSD End to End Detected Counts", 4}, - { "SSD End to End Corrected Errors", 4}, - { "System data % life-used", 1}, - { "Refresh Count", 7}, - { "Maximum User Data Erase Count", 4}, - { "Minimum User Data Erase Count", 4}, - { "Thermal Throttling Count", 1}, - { "Thermal Throttling Status", 1}, - { "Reserved", 6}, - { "PCIe Correctable Error count", 8}, - { "Incomplete Shutdowns", 4}, - { "Reserved", 4}, - { "% Free Blocks", 1}, - { "Reserved", 7}, - { "Capacitor Health", 2}, - { "Reserved", 6}, - { "Unaligned I/O", 8}, - { "Security Version Number", 8}, - { "NUSE", 8}, - { "PLP Start Count", 16}, - { "Endurance Estimate", 16}, - { "Reserved", 302}, - { "Log Page Version", 2}, - { "Log Page GUID", 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}, + { "Raw Bad System NAND Block Count", 6}, + { "Normalized Bad System NAND Block Count", 2}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "Soft ECC Error Count", 8}, + { "SSD End to End Detected Counts", 4}, + { "SSD End to End Corrected Errors", 4}, + { "System data % life-used", 1}, + { "Refresh Count", 7}, + { "Maximum User Data Erase Count", 4}, + { "Minimum User Data Erase Count", 4}, + { "Thermal Throttling Count", 1}, + { "Thermal Throttling Status", 1}, + { "Reserved", 6}, + { "PCIe Correctable Error count", 8}, + { "Incomplete Shutdowns", 4}, + { "Reserved", 4}, + { "% Free Blocks", 1}, + { "Reserved", 7}, + { "Capacitor Health", 2}, + { "Reserved", 6}, + { "Unaligned I/O", 8}, + { "Security Version Number", 8}, + { "NUSE", 8}, + { "PLP Start Count", 16}, + { "Endurance Estimate", 16}, + { "Reserved", 302}, + { "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}, + { "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 }, - { "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 Status", 1, 1}, - { "Thermal Throttling Count", 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}, + { "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 Status", 1, 1}, + { "Thermal Throttling Count", 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 */ -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 */ - __u8 spec /* ocp spec index */ -) +/* + * Common function to print Micron VS log pages + * - buf: raw log data + * - log_page: format of the data + * - field_count: log field count + * - stats: json object to add fields + * - spec: ocp spec index + */ +static void print_micron_vs_logs(__u8 *buf, struct micron_vs_logpage *log_page, int field_count, + struct json_object *stats, __u8 spec) { - __u64 lval_lo, lval_hi; - __u32 ival; - __u16 sval; - __u8 cval, lval[8] = { 0 }; - int field; - int offset = 0; - - for (field = 0; field < field_count; field++) { - char datastr[1024] = { 0 }; - 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%"PRIx64"%"PRIx64"", - (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"%016"PRIx64"", - le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); - else - sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } - } else if (size == 8) { - lval_lo = *((__u64 *)(&buf[offset])); - sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); - } 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 (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 (size == 4) { - ival = *((__u32 *)(&buf[offset])); - sprintf(datastr, "0x%x", le32_to_cpu(ival)); - } else if (size == 2) { - sval = *((__u16 *)(&buf[offset])); - sprintf(datastr, "0x%04x", le16_to_cpu(sval)); - } else if (size == 1) { - cval = buf[offset]; - sprintf(datastr, "0x%02x", cval); - } else { - sprintf(datastr, "0"); - } - offset += size; - /* do not print reserved values */ - if (strstr(sfield, "Reserved")) - continue; - if (stats != NULL) { - json_object_add_value_string(stats, sfield, datastr); - } else { - printf("%-40s : %-4s\n", sfield, datastr); - } - } + __u64 lval_lo, lval_hi; + __u32 ival; + __u16 sval; + __u8 cval, lval[8] = { 0 }; + int field; + int offset = 0; + + for (field = 0; field < field_count; field++) { + char datastr[1024] = { 0 }; + char *sfield = NULL; + int size = !spec ? log_page[field].size : log_page[field].size2; + + if (!size) + continue; + sfield = log_page[field].field; + if (size == 16) { + if (strstr(sfield, "GUID")) { + sprintf(datastr, "0x%"PRIx64"%"PRIx64"", + (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"%016"PRIx64"", + le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); + else + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } + } else if (size == 8) { + lval_lo = *((__u64 *)(&buf[offset])); + sprintf(datastr, "0x%"PRIx64"", le64_to_cpu(lval_lo)); + } 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 (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 (size == 4) { + ival = *((__u32 *)(&buf[offset])); + sprintf(datastr, "0x%x", le32_to_cpu(ival)); + } else if (size == 2) { + sval = *((__u16 *)(&buf[offset])); + sprintf(datastr, "0x%04x", le16_to_cpu(sval)); + } else if (size == 1) { + cval = buf[offset]; + sprintf(datastr, "0x%02x", cval); + } else { + sprintf(datastr, "0"); + } + offset += size; + /* do not print reserved values */ + if (strstr(sfield, "Reserved")) + continue; + if (stats) + json_object_add_value_string(stats, sfield, datastr); + else + printf("%-40s : %-4s\n", sfield, datastr); + } } static void print_smart_cloud_health_log(__u8 *buf, bool is_json) { - struct json_object *root; - struct json_object *logPages; - struct json_object *stats = NULL; - int field_count = sizeof(ocp_c0_log_page)/sizeof(ocp_c0_log_page[0]); - - if (is_json) { - root = json_create_object(); - stats = json_create_object(); - logPages = json_create_array(); - json_object_add_value_array(root, "OCP SMART Cloud Health Log: 0xC0", - logPages); - } - - print_micron_vs_logs(buf, ocp_c0_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); - } + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = ARRAY_SIZE(ocp_c0_log_page); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "OCP SMART Cloud Health Log: 0xC0", + logPages); + } + + print_micron_vs_logs(buf, ocp_c0_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 void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool is_json, __u8 spec) { - struct json_object *root; - struct json_object *logPages; - struct json_object *stats = NULL; - int field_count = sizeof(fb_log_page)/sizeof(fb_log_page[0]); - - if (is_json) { - root = json_create_object(); - stats = json_create_object(); - logPages = json_create_array(); - json_object_add_value_array(root, "Extended Smart Log Page : 0xFB", - logPages); - } - - print_micron_vs_logs(buf, fb_log_page, field_count, stats, spec); - - /* print last three entries from D0 log page */ - if (buf2 != NULL) { - init_d0_log_page(buf2, nsze); - - if (is_json) { - for (int i = 0; i < 7; i++) { - json_object_add_value_string(stats, - d0_log_page[i].field, - d0_log_page[i].datastr); - } - } else { - for (int i = 0; i < 7; i++) { - printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); - } - } - } - - if (is_json) { - json_array_add_value_object(logPages, stats); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = ARRAY_SIZE(fb_log_page); + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Extended Smart Log Page : 0xFB", + logPages); + } + + print_micron_vs_logs(buf, fb_log_page, field_count, stats, spec); + + /* print last three entries from D0 log page */ + if (buf2) { + init_d0_log_page(buf2, nsze); + + if (is_json) { + for (int i = 0; i < 7; i++) + json_object_add_value_string(stats, + d0_log_page[i].field, + d0_log_page[i].datastr); + } else { + for (int i = 0; i < 7; i++) + printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); + } + } + + if (is_json) { + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } } static void print_nand_stats_d0(__u8 *buf, __u8 oacs, bool is_json) { - init_d0_log_page(buf, oacs); - - if (is_json) { - struct json_object *root = json_create_object(); - struct json_object *stats = json_create_object(); - struct json_object *logPages = json_create_array(); - - json_object_add_value_array(root, - "Extended Smart Log Page : 0xD0", - logPages); - - for (int i = 0; i < 7; i++) { - json_object_add_value_string(stats, - d0_log_page[i].field, - d0_log_page[i].datastr); - } - - json_array_add_value_object(logPages, stats); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } else { - for (int i = 0; i < 7; i++) { - printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); - } - } + init_d0_log_page(buf, oacs); + + if (is_json) { + struct json_object *root = json_create_object(); + struct json_object *stats = json_create_object(); + struct json_object *logPages = json_create_array(); + + json_object_add_value_array(root, + "Extended Smart Log Page : 0xD0", + logPages); + + for (int i = 0; i < 7; i++) + json_object_add_value_string(stats, + d0_log_page[i].field, + d0_log_page[i].datastr); + + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + for (int i = 0; i < 7; i++) + printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr); + } } -static bool nsze_from_oacs = false; /* read nsze for now from idd[4059] */ +static bool nsze_from_oacs; /* read nsze for now from idd[4059] */ static int micron_nand_stats(int argc, char **argv, - struct command *cmd, struct plugin *plugin) + struct command *cmd, struct plugin *plugin) { - const char *desc = "Retrieve Micron NAND stats for the given device "; - unsigned int extSmartLog[D0_log_size/sizeof(int)] = { 0 }; - unsigned int logFB[FB_log_size/sizeof(int)] = { 0 }; - eDriveModel eModel = UNKNOWN_MODEL; - struct nvme_id_ctrl ctrl; - struct nvme_dev *dev; - int err, ctrlIdx; - __u8 nsze; - bool has_d0_log = true; - bool has_fb_log = false; - 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() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) { - printf("\nDevice not found \n");; - return -1; - } - - if (strcmp(cfg.fmt, "normal") == 0) - is_json = false; - - err = nvme_identify_ctrl(dev_fd(dev), &ctrl); - if (err) { - printf("Error %d retrieving controller identification data\n", err); - goto out; - } - - /* pull log details based on the model name */ - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - eModel = GetDriveModel(ctrlIdx); - if ((eModel == UNKNOWN_MODEL) || (eModel == M51CX)) { - printf ("Unsupported drive model for vs-nand-stats command\n"); - err = -1; - goto out; - } - - err = nvme_get_log_simple(dev_fd(dev), 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_simple(dev_fd(dev), 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); - } else { - printf("Unable to retrieve extended smart log for the drive\n"); - err = -ENOTTY; - } + const char *desc = "Retrieve Micron NAND stats for the given device "; + unsigned int extSmartLog[D0_log_size/sizeof(int)] = { 0 }; + unsigned int logFB[FB_log_size/sizeof(int)] = { 0 }; + enum eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + struct nvme_dev *dev; + int err, ctrlIdx; + __u8 nsze; + bool has_d0_log = true; + bool has_fb_log = false; + 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() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("\nDevice not found\n"); + return -1; + } + + if (!strcmp(cfg.fmt, "normal")) + is_json = false; + + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err) { + printf("Error %d retrieving controller identification data\n", err); + goto out; + } + + /* pull log details based on the model name */ + if (sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx) != 1) + ctrlIdx = 0; + eModel = GetDriveModel(ctrlIdx); + if ((eModel == UNKNOWN_MODEL) || (eModel == M51CX)) { + printf("Unsupported drive model for vs-nand-stats command\n"); + err = -1; + goto out; + } + + err = nvme_get_log_simple(dev_fd(dev), 0xD0, D0_log_size, extSmartLog); + has_d0_log = !err; + + /* should check for firmware version if this log is supported or not */ + if (eModel == M5407 || eModel == M5410) { + err = nvme_get_log_simple(dev_fd(dev), 0xFB, FB_log_size, logFB); + has_fb_log = !err; + } + + nsze = (ctrl.vs[987] == 0x12); + if (!nsze && 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); + } else { + printf("Unable to retrieve extended smart log for the drive\n"); + err = -ENOTTY; + } out: - dev_close(dev); - if (err > 0) - nvme_show_status(err); + dev_close(dev); + if (err > 0) + nvme_show_status(err); - return 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); - } + struct json_object *root; + struct json_object *logPages; + struct json_object *stats = NULL; + int field_count = ARRAY_SIZE(e1_log_page); + + 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) + 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 err = 0, ctrlIdx = 0; - struct nvme_dev *dev; - 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() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) { - 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(dev_fd(dev), 0xE1, E1_log_size, extSmartLog); - if (!err) { - print_ext_smart_logs_e1((__u8 *)extSmartLog, is_json); - } + const char *desc = "Retrieve extended SMART logs for the given device "; + unsigned int extSmartLog[E1_log_size/sizeof(int)] = { 0 }; + enum eDriveModel eModel = UNKNOWN_MODEL; + int err = 0, ctrlIdx; + struct nvme_dev *dev; + 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() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) { + printf("\nDevice not found\n"); + return -1; + } + if (!strcmp(cfg.fmt, "normal")) + is_json = false; + + if (sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx) != 1) + ctrlIdx = 0; + eModel = GetDriveModel(ctrlIdx); + if (eModel != M51CX) { + printf("Unsupported drive model for vs-smart-ext-log command\n"); + err = -1; + goto out; + } + err = nvme_get_log_simple(dev_fd(dev), 0xE1, E1_log_size, extSmartLog); + if (!err) + print_ext_smart_logs_e1((__u8 *)extSmartLog, is_json); out: - dev_close(dev); - if (err > 0) - nvme_show_status(err); - return err; + dev_close(dev); + if (err > 0) + nvme_show_status(err); + return err; } static void GetDriveInfo(const char *strOSDirName, int nFD, - struct nvme_id_ctrl *ctrlp) + struct nvme_id_ctrl *ctrlp) { - FILE *fpOutFile = NULL; - char tempFile[256] = { 0 }; - char strBuffer[1024] = { 0 }; - char model[41] = { 0 }; - char serial[21] = { 0 }; - char fwrev[9] = { 0 }; - char *strPDir = strdup(strOSDirName); - char *strDest = dirname(strPDir); - - sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); - fpOutFile = fopen(tempFile, "w+"); - if (!fpOutFile) { - printf("Failed to create %s\n", tempFile); - free(strPDir); - return; - } - - strncpy(model, ctrlp->mn, 40); - strncpy(serial, ctrlp->sn, 20); - strncpy(fwrev, ctrlp->fr, 8); - - sprintf(strBuffer, - "********************\nDrive Info\n********************\n"); - - fprintf(fpOutFile, "%s", strBuffer); - sprintf(strBuffer, - "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", - "Device Name", nFD, - "Model No", (char *)model, - "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); - - fprintf(fpOutFile, "%s", strBuffer); - - sprintf(strBuffer, - "\n********************\nPCI Info\n********************\n"); - - fprintf(fpOutFile, "%s", strBuffer); - - sprintf(strBuffer, - "%-22s : %04X\n%-22s : %04X\n", - "VendorId", vendor_id, "DeviceId", device_id); - fprintf(fpOutFile, "%s", strBuffer); - fclose(fpOutFile); - free(strPDir); + FILE *fpOutFile = NULL; + char tempFile[256] = { 0 }; + char strBuffer[1024] = { 0 }; + char model[41] = { 0 }; + char serial[21] = { 0 }; + char fwrev[9] = { 0 }; + char *strPDir = strdup(strOSDirName); + char *strDest = dirname(strPDir); + + sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); + fpOutFile = fopen(tempFile, "w+"); + if (!fpOutFile) { + printf("Failed to create %s\n", tempFile); + free(strPDir); + return; + } + + strncpy(model, ctrlp->mn, 40); + strncpy(serial, ctrlp->sn, 20); + strncpy(fwrev, ctrlp->fr, 8); + + sprintf(strBuffer, + "********************\nDrive Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + sprintf(strBuffer, + "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", + "Device Name", nFD, + "Model No", (char *)model, + "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "\n********************\nPCI Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "%-22s : %04X\n%-22s : %04X\n", + "VendorId", vendor_id, "DeviceId", device_id); + fprintf(fpOutFile, "%s", strBuffer); + fclose(fpOutFile); + free(strPDir); } static void GetTimestampInfo(const char *strOSDirName) { - __u8 outstr[1024]; - time_t t; - struct tm *tmp; - size_t num; - char *strPDir; - char *strDest; - - t = time(NULL); - tmp = localtime(&t); - if (tmp == NULL) - return; - - num = strftime((char *)outstr, sizeof(outstr), - "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); - num += sprintf((char *)(outstr + num), "\nPackage Version: 1.4"); - if (num) { - strPDir = strdup(strOSDirName); - strDest = dirname(strPDir); - WriteData(outstr, num, strDest, "timestamp_info.txt", "timestamp"); - free(strPDir); - } + __u8 outstr[1024]; + time_t t; + struct tm *tmp; + size_t num; + char *strPDir; + char *strDest; + + t = time(NULL); + tmp = localtime(&t); + if (!tmp) + return; + + num = strftime((char *)outstr, sizeof(outstr), + "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); + num += sprintf((char *)(outstr + num), "\nPackage Version: 1.4"); + if (num) { + strPDir = strdup(strOSDirName); + strDest = dirname(strPDir); + WriteData(outstr, num, strDest, "timestamp_info.txt", "timestamp"); + free(strPDir); + } } static void GetCtrlIDDInfo(const char *dir, struct nvme_id_ctrl *ctrlp) { - WriteData((__u8*)ctrlp, sizeof(*ctrlp), dir, - "nvme_controller_identify_data.bin", "id-ctrl"); + WriteData((__u8 *)ctrlp, sizeof(*ctrlp), dir, + "nvme_controller_identify_data.bin", "id-ctrl"); } static void GetSmartlogData(int fd, const char *dir) { - struct nvme_smart_log smart_log; - if (nvme_get_log_smart(fd, -1, false, &smart_log) == 0) { - WriteData((__u8*)&smart_log, sizeof(smart_log), dir, - "smart_data.bin", "smart log"); - } + struct nvme_smart_log smart_log; + + if (!nvme_get_log_smart(fd, -1, false, &smart_log)) + WriteData((__u8 *)&smart_log, sizeof(smart_log), dir, + "smart_data.bin", "smart log"); } static void GetErrorlogData(int fd, int entries, const char *dir) { - int logSize = entries * sizeof(struct nvme_error_log_page); - struct nvme_error_log_page *error_log = - (struct nvme_error_log_page *)calloc(1, logSize); + int logSize = entries * sizeof(struct nvme_error_log_page); + struct nvme_error_log_page *error_log = + (struct nvme_error_log_page *)calloc(1, logSize); - if (error_log == NULL) - return; + if (!error_log) + return; - if (nvme_get_log_error(fd, entries, false, error_log) == 0) { - WriteData((__u8*)error_log, logSize, dir, - "error_information_log.bin", "error log"); - } + if (!nvme_get_log_error(fd, entries, false, error_log)) + WriteData((__u8 *)error_log, logSize, dir, + "error_information_log.bin", "error log"); - free(error_log); + free(error_log); } static void GetGenericLogs(int fd, const char *dir) { - struct nvme_self_test_log self_test_log; - 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_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_get_log_fw_slot(fd, false, &fw_log) == 0) { - WriteData((__u8*)&fw_log, sizeof(fw_log), dir, - "firmware_slot_info_log.bin", "firmware log"); - } - - /* get effects log */ - 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_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"); - return; - } - - 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"); - return; - } - 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, - "persistent_event_log.bin", "persistent event log"); - } - nvme_free(pevent_log_info, huge); - return; + struct nvme_self_test_log self_test_log; + 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_get_log_device_self_test(fd, &self_test_log)) + WriteData((__u8 *)&self_test_log, sizeof(self_test_log), dir, + "drive_self_test.bin", "self test log"); + + /* get fw slot info log */ + if (!nvme_get_log_fw_slot(fd, false, &fw_log)) + WriteData((__u8 *)&fw_log, sizeof(fw_log), dir, + "firmware_slot_info_log.bin", "firmware log"); + + /* get effects log */ + if (!nvme_get_log_cmd_effects(fd, NVME_CSI_NVM, &effects)) + WriteData((__u8 *)&effects, sizeof(effects), dir, + "command_effects_log.bin", "effects log"); + + /* get persistent event log */ + (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"); + return; + } + + 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"); + return; + } + err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_READ, + log_len, pevent_log_info); + if (!err) + WriteData((__u8 *)pevent_log_info, log_len, dir, + "persistent_event_log.bin", "persistent event log"); + nvme_free(pevent_log_info, huge); } static void GetNSIDDInfo(int fd, const char *dir, int nsid) { - char file[PATH_MAX] = { 0 }; - struct nvme_id_ns ns; + char file[PATH_MAX] = { 0 }; + struct nvme_id_ns ns; - 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"); - } + if (!nvme_identify_ns(fd, nsid, &ns)) { + sprintf(file, "identify_namespace_%d_data.bin", nsid); + WriteData((__u8 *)&ns, sizeof(ns), dir, file, "id-ns"); + } } static void GetOSConfig(const char *strOSDirName) { - FILE *fpOSConfig = NULL; - char strBuffer[1024]; - char strFileName[PATH_MAX]; - int i; - - struct { - char *strcmdHeader; - char *strCommand; - } cmdArray[] = { - { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, - { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, - { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, - { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, - { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, - { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, - { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, - { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, - { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } - }; - - sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); - - for (i = 0; i < 7; i++) { - fpOSConfig = fopen(strFileName, "a+"); - if (NULL != fpOSConfig) { - fprintf(fpOSConfig, - "\n\n\n\n%s\n-----------------------------------------------\n", - cmdArray[i].strcmdHeader); - fclose(fpOSConfig); - fpOSConfig = NULL; - } - snprintf(strBuffer, sizeof(strBuffer) - 1, - cmdArray[i].strCommand, strFileName); - if (system(strBuffer)) - fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); - } + FILE *fpOSConfig = NULL; + char strBuffer[1024]; + char strFileName[PATH_MAX]; + int i; + + struct { + char *strcmdHeader; + char *strCommand; + } cmdArray[] = { + { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, + { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, + { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, + { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, + { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, + { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, + { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, + { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, + { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } + }; + + sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); + + for (i = 0; i < 7; i++) { + fpOSConfig = fopen(strFileName, "a+"); + if (fpOSConfig) { + fprintf(fpOSConfig, + "\n\n\n\n%s\n-----------------------------------------------\n", + cmdArray[i].strcmdHeader); + fclose(fpOSConfig); + fpOSConfig = NULL; + } + snprintf(strBuffer, sizeof(strBuffer) - 1, + cmdArray[i].strCommand, strFileName); + if (system(strBuffer)) + fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); + } } static int micron_telemetry_log(int fd, __u8 type, __u8 **data, - int *logSize, int da) + int *logSize, int da) { - int err, bs = 512, offset = bs; - unsigned short data_area[4]; - unsigned char ctrl_init = (type == 0x8); - - __u8 *buffer = (unsigned char *)calloc(bs, 1); - if (buffer == NULL) - return -1; - 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) { - free(buffer); - } - return err; - } - - /* 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]; - - if (data_area[da] == 0) { - fprintf(stderr, "Requested telemetry data for 0x%X is empty\n", type); - if (buffer != NULL) { - free(buffer); - buffer = NULL; - } - return -1; - } - - *logSize = data_area[da] * bs; - offset = bs; - err = 0; - if ((buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize))) != NULL) { - while (err == 0 && offset != *logSize) { - 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; - } - } - - if (err == 0 && buffer != NULL) { - *data = buffer; - } else { - fprintf(stderr, "Failed to get telemetry data for 0x%x\n", type); - if (buffer != NULL) - free(buffer); - } - - return err; + int err, bs = 512, offset = bs; + unsigned short data_area[4]; + unsigned char ctrl_init = (type == 0x8); + + __u8 *buffer = (unsigned char *)calloc(bs, 1); + + if (!buffer) + return -1; + 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) { + fprintf(stderr, "Failed to get telemetry log header for 0x%X\n", type); + if (buffer) + free(buffer); + return err; + } + + /* 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]; + + if (!data_area[da]) { + fprintf(stderr, "Requested telemetry data for 0x%X is empty\n", type); + if (buffer) { + free(buffer); + buffer = NULL; + } + return -1; + } + + *logSize = data_area[da] * bs; + offset = bs; + err = 0; + buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize)); + if (buffer) { + while (!err && offset != *logSize) { + 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; + } + } + + if (!err && buffer) { + *data = buffer; + } else { + fprintf(stderr, "Failed to get telemetry data for 0x%x\n", type); + if (buffer) + free(buffer); + } + + return err; } static int GetTelemetryData(int fd, const char *dir) { - unsigned char *buffer = NULL; - int i, err, logSize = 0; - char msg[256] = {0}; - struct { - __u8 log; - char *file; - } tmap[] = { - {0x07, "nvmetelemetrylog.bin"}, - {0x08, "nvmetelemetrylog.bin"}, - }; - - for(i = 0; i < (int)(sizeof(tmap)/sizeof(tmap[0])); i++) { - 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); - } - if (buffer) { - free(buffer); - buffer = NULL; - } - logSize = 0; - } - return err; + unsigned char *buffer = NULL; + int i, err, logSize = 0; + char msg[256] = { 0 }; + struct { + __u8 log; + char *file; + } tmap[] = { + {0x07, "nvmetelemetrylog.bin"}, + {0x08, "nvmetelemetrylog.bin"}, + }; + + for (i = 0; i < (int)(ARRAY_SIZE(tmap)); i++) { + err = micron_telemetry_log(fd, tmap[i].log, &buffer, &logSize, 0); + if (!err && logSize > 0 && buffer) { + sprintf(msg, "telemetry log: 0x%X", tmap[i].log); + WriteData(buffer, logSize, dir, tmap[i].file, msg); + } + if (buffer) { + free(buffer); + buffer = NULL; + } + logSize = 0; + } + return err; } static int GetFeatureSettings(int fd, const char *dir) { - unsigned char *bufp, buf[4096] = { 0 }; - int i, err, len, errcnt = 0; - __u32 attrVal = 0; - char msg[256] = { 0 }; - - struct features { - int id; - char *file; - } fmap[] = { - {0x01, "nvme_feature_setting_arbitration.bin"}, - {0x02, "nvme_feature_setting_pm.bin"}, - {0x03, "nvme_feature_setting_lba_range_namespace_1.bin"}, - {0x04, "nvme_feature_setting_temp_threshold.bin"}, - {0x05, "nvme_feature_setting_error_recovery.bin"}, - {0x06, "nvme_feature_setting_volatile_write_cache.bin"}, - {0x07, "nvme_feature_setting_num_queues.bin"}, - {0x08, "nvme_feature_setting_interrupt_coalescing.bin"}, - {0x09, "nvme_feature_setting_interrupt_vec_config.bin"}, - {0x0A, "nvme_feature_setting_write_atomicity.bin"}, - {0x0B, "nvme_feature_setting_async_event_config.bin"}, - {0x80, "nvme_feature_setting_sw_progress_marker.bin"}, - }; - - for (i = 0; i < (int)(sizeof(fmap)/sizeof(fmap[0])); i++) { - if (fmap[i].id == 0x03) { - len = 4096; - bufp = (unsigned char *)(&buf[0]); - } else { - len = 0; - bufp = NULL; - } + unsigned char *bufp, buf[4096] = { 0 }; + int i, err, len, errcnt = 0; + __u32 attrVal = 0; + char msg[256] = { 0 }; + + struct features { + int id; + char *file; + } fmap[] = { + {0x01, "nvme_feature_setting_arbitration.bin"}, + {0x02, "nvme_feature_setting_pm.bin"}, + {0x03, "nvme_feature_setting_lba_range_namespace_1.bin"}, + {0x04, "nvme_feature_setting_temp_threshold.bin"}, + {0x05, "nvme_feature_setting_error_recovery.bin"}, + {0x06, "nvme_feature_setting_volatile_write_cache.bin"}, + {0x07, "nvme_feature_setting_num_queues.bin"}, + {0x08, "nvme_feature_setting_interrupt_coalescing.bin"}, + {0x09, "nvme_feature_setting_interrupt_vec_config.bin"}, + {0x0A, "nvme_feature_setting_write_atomicity.bin"}, + {0x0B, "nvme_feature_setting_async_event_config.bin"}, + {0x80, "nvme_feature_setting_sw_progress_marker.bin"}, + }; + + for (i = 0; i < (int)(ARRAY_SIZE(fmap)); i++) { + if (fmap[i].id == 0x03) { + len = 4096; + bufp = (unsigned char *)(&buf[0]); + } else { + len = 0; + bufp = NULL; + } struct nvme_get_features_args args = { .args_size = sizeof(args), @@ -1995,901 +2001,882 @@ static int GetFeatureSettings(int fd, const char *dir) .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); - if (bufp != NULL) { - WriteData(bufp, len, dir, fmap[i].file, msg); - } - } else { - fprintf(stderr, "Feature 0x%x data not retrieved, error %d (ignored)!\n", - fmap[i].id, err); - errcnt++; - } - } - return (int)(errcnt == sizeof(fmap)/sizeof(fmap[0])); + err = nvme_get_features(&args); + if (!err) { + sprintf(msg, "feature: 0x%X", fmap[i].id); + WriteData((__u8 *)&attrVal, sizeof(attrVal), dir, fmap[i].file, msg); + if (bufp) + WriteData(bufp, len, dir, fmap[i].file, msg); + } else { + fprintf(stderr, "Feature 0x%x data not retrieved, error %d (ignored)!\n", + fmap[i].id, err); + errcnt++; + } + } + return (int)(errcnt == ARRAY_SIZE(fmap)); } static int micron_drive_info(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - const char *desc = "Get drive HW information"; - struct nvme_id_ctrl ctrl = { 0 }; - struct nvme_passthru_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; - bool is_json = false; - struct json_object *root, *driveInfo; - struct nvme_dev *dev; - struct format { - char *fmt; - }; - int err = 0; - - const char *fmt = "output format normal"; - struct format cfg = { - .fmt = "normal", - }; - - OPT_ARGS(opts) = { - OPT_FMT("format", 'f', &cfg.fmt, fmt), - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 0) - return err; - - if (model == UNKNOWN_MODEL) { - fprintf(stderr, "ERROR : Unsupported drive for vs-drive-info cmd"); - dev_close(dev); - return -1; - } - - if (strcmp(cfg.fmt, "json") == 0) - is_json = true; - - 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 = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL); - if (err) { - fprintf(stderr, "ERROR : drive-info opcode failed with 0x%x\n", err); - dev_close(dev); - return -1; - } - } else { - err = nvme_identify_ctrl(dev_fd(dev), &ctrl); - if (err) { - fprintf(stderr, "ERROR : identify_ctrl() failed with 0x%x\n", err); - dev_close(dev); - return -1; - } - dinfo.hw_ver_major = ctrl.vs[820]; - dinfo.hw_ver_minor = ctrl.vs[821]; - dinfo.ftl_unit_size = ctrl.vs[822]; - } - - if (is_json) { - struct json_object *pinfo = json_create_object(); - char tempstr[64] = { 0 }; - root = json_create_object(); - driveInfo = json_create_array(); - json_object_add_value_array(root, "Micron Drive HW Information", driveInfo); - sprintf(tempstr, "%hhu.%hhu", dinfo.hw_ver_major, dinfo.hw_ver_minor); - json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr); - - if (dinfo.ftl_unit_size) { - sprintf(tempstr, "%hhu KB", dinfo.ftl_unit_size); - json_object_add_value_string(pinfo, "FTL_unit_size", tempstr); - } - - if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) { - sprintf(tempstr, "%hhu.%hhu", dinfo.bs_ver_major, dinfo.bs_ver_minor); - json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr); - } - - json_array_add_value_object(driveInfo, pinfo); - json_print_object(root, NULL); - printf("\n"); - json_free_object(root); - } else { - 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); - } - } - - dev_close(dev); - return 0; + const char *desc = "Get drive HW information"; + struct nvme_id_ctrl ctrl = { 0 }; + struct nvme_passthru_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 }; + enum eDriveModel model = UNKNOWN_MODEL; + bool is_json = false; + struct json_object *root, *driveInfo; + struct nvme_dev *dev; + struct format { + char *fmt; + }; + int err = 0; + + const char *fmt = "output format normal"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 0) + return err; + + if (model == UNKNOWN_MODEL) { + fprintf(stderr, "ERROR : Unsupported drive for vs-drive-info cmd"); + dev_close(dev); + return -1; + } + + if (!strcmp(cfg.fmt, "json")) + is_json = true; + + 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 = nvme_submit_admin_passthru(dev_fd(dev), &admin_cmd, NULL); + if (err) { + fprintf(stderr, "ERROR : drive-info opcode failed with 0x%x\n", err); + dev_close(dev); + return -1; + } + } else { + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err) { + fprintf(stderr, "ERROR : identify_ctrl() failed with 0x%x\n", err); + dev_close(dev); + return -1; + } + dinfo.hw_ver_major = ctrl.vs[820]; + dinfo.hw_ver_minor = ctrl.vs[821]; + dinfo.ftl_unit_size = ctrl.vs[822]; + } + + if (is_json) { + struct json_object *pinfo = json_create_object(); + char tempstr[64] = { 0 }; + + root = json_create_object(); + driveInfo = json_create_array(); + json_object_add_value_array(root, "Micron Drive HW Information", driveInfo); + sprintf(tempstr, "%u.%u", dinfo.hw_ver_major, dinfo.hw_ver_minor); + json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr); + + if (dinfo.ftl_unit_size) { + sprintf(tempstr, "%u KB", dinfo.ftl_unit_size); + json_object_add_value_string(pinfo, "FTL_unit_size", tempstr); + } + + if (dinfo.bs_ver_major || dinfo.bs_ver_minor) { + sprintf(tempstr, "%u.%u", dinfo.bs_ver_major, dinfo.bs_ver_minor); + json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr); + } + + json_array_add_value_object(driveInfo, pinfo); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + printf("Drive Hardware Version: %u.%u\n", + dinfo.hw_ver_major, dinfo.hw_ver_minor); + + if (dinfo.ftl_unit_size) + printf("FTL_unit_size: %u KB\n", dinfo.ftl_unit_size); + + if (dinfo.bs_ver_major || dinfo.bs_ver_minor) + printf("Boot Spec.Version: %u.%u\n", + dinfo.bs_ver_major, dinfo.bs_ver_minor); + } + + dev_close(dev); + return 0; } static int micron_cloud_ssd_plugin_version(int argc, char **argv, - struct command *cmd, struct plugin *plugin) + struct command *cmd, struct plugin *plugin) { - printf("nvme-cli Micron cloud SSD plugin version: %s.%s\n", - __version_major, __version_minor); - return 0; + printf("nvme-cli Micron cloud SSD plugin version: %s.%s\n", + __version_major, __version_minor); + return 0; } static int micron_plugin_version(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - printf("nvme-cli Micron plugin version: %s.%s.%s\n", - __version_major, __version_minor, __version_patch); - return 0; + printf("nvme-cli Micron plugin version: %s.%s.%s\n", + __version_major, __version_minor, __version_patch); + return 0; } /* Binary format of firmware activation history entry */ -struct __attribute__((__packed__)) fw_activation_history_entry { - __u8 version; - __u8 length; - __u16 rsvd1; - __le16 valid; - __le64 power_on_hour; - __le64 rsvd2; - __le64 power_cycle_count; - __u8 previous_fw[8]; - __u8 activated_fw[8]; - __u8 slot; - __u8 commit_action_type; - __le16 result; - __u8 rsvd3[14]; +struct __packed fw_activation_history_entry { + __u8 version; + __u8 length; + __u16 rsvd1; + __le16 valid; + __le64 power_on_hour; + __le64 rsvd2; + __le64 power_cycle_count; + __u8 previous_fw[8]; + __u8 activated_fw[8]; + __u8 slot; + __u8 commit_action_type; + __le16 result; + __u8 rsvd3[14]; }; - /* Binary format for firmware activation history table */ -struct __attribute__((__packed__)) micron_fw_activation_history_table { - __u8 log_page; - __u8 rsvd1[3]; - __le32 num_entries; - struct fw_activation_history_entry entries[20]; - __u8 rsvd2[2790]; - __u16 version; - __u8 GUID[16]; +struct __packed micron_fw_activation_history_table { + __u8 log_page; + __u8 rsvd1[3]; + __le32 num_entries; + struct fw_activation_history_entry entries[20]; + __u8 rsvd2[2790]; + __u16 version; + __u8 GUID[16]; }; -/* header to be printed field widths = 10 | 12 | 10 | 11 | 12 | 9 | 9 | 9 */ - -const char *fw_activation_history_table_header = "\ -__________________________________________________________________________________\n\ - | | | | | | | \n\ -Firmware | Power On | Power | Previous | New FW | Slot | Commit | Result \n\ -Activation| Hour | cycle | firmware | activated | number | Action | \n\ -Counter | | count | | | | Type | \n\ -__________|___________|_________|__________|___________|________|________|________\n"; - -static int display_fw_activate_entry ( - int entry_count, - struct fw_activation_history_entry *entry, - char *formatted_entry, - struct json_object *stats -) +static int display_fw_activate_entry(int entry_count, struct fw_activation_history_entry *entry, + char *formatted_entry, struct json_object *stats) { - time_t timestamp, hours; - char buffer[32]; - __u8 minutes, seconds; - char *ca[] = {"000b", "001b", "010b", "011b"}; - char *ptr = formatted_entry; - int index = 0, entry_size = 82; - - if ((entry->version != 1 && entry->version != 2) || entry->length != 64) { - /*fprintf(stderr, "unsupported entry ! version: %x with length: %d\n", - entry->version, entry->length); */ - return -EINVAL; - } - - sprintf(ptr, "%d", entry_count); - ptr += 10; - - timestamp = (le64_to_cpu(entry->power_on_hour) & 0x0000FFFFFFFFFFFFUL) / 1000; - hours = timestamp / 3600; - minutes = (timestamp % 3600) / 60; - seconds = (timestamp % 3600) % 60; - sprintf(ptr, "|%"PRIu64":%hhu:%hhu", (uint64_t)hours, minutes, seconds); - ptr += 12; - - sprintf(ptr, "| %"PRIu64, le64_to_cpu(entry->power_cycle_count)); - ptr += 10; - - /* firmware details */ - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, entry->previous_fw, sizeof(entry->previous_fw)); - sprintf(ptr, "| %s", buffer); - ptr += 11; - - memset(buffer, 0, sizeof(buffer)); - memcpy(buffer, entry->activated_fw, sizeof(entry->activated_fw)); - sprintf(ptr, "| %s", buffer); - ptr += 12; - - /* firmware slot and commit action*/ - sprintf(ptr, "| %d", entry->slot); - ptr += 9; - - if (entry->commit_action_type <= 3) - sprintf(ptr, "| %s", ca[entry->commit_action_type]); - else - sprintf(ptr, "| xxxb"); - ptr += 9; - - /* result */ - if (entry->result) { - sprintf(ptr, "| Fail #%d", entry->result); - } else { - sprintf(ptr, "| pass"); - } - - /* replace all null charecters with spaces */ - ptr = formatted_entry; - while (index < entry_size) { - if (ptr[index] == '\0') - ptr[index] = ' '; - index++; - } - return 0; + time_t timestamp, hours; + char buffer[32]; + __u8 minutes, seconds; + static const char * const ca[] = {"000b", "001b", "010b", "011b"}; + char *ptr = formatted_entry; + int index = 0, entry_size = 82; + + if ((entry->version != 1 && entry->version != 2) || entry->length != 64) + return -EINVAL; + + sprintf(ptr, "%d", entry_count); + ptr += 10; + + timestamp = (le64_to_cpu(entry->power_on_hour) & 0x0000FFFFFFFFFFFFUL) / 1000; + hours = timestamp / 3600; + minutes = (timestamp % 3600) / 60; + seconds = (timestamp % 3600) % 60; + sprintf(ptr, "|%"PRIu64":%u:%u", (uint64_t)hours, minutes, seconds); + ptr += 12; + + sprintf(ptr, "| %"PRIu64, le64_to_cpu(entry->power_cycle_count)); + ptr += 10; + + /* firmware details */ + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, entry->previous_fw, sizeof(entry->previous_fw)); + sprintf(ptr, "| %s", buffer); + ptr += 11; + + memset(buffer, 0, sizeof(buffer)); + memcpy(buffer, entry->activated_fw, sizeof(entry->activated_fw)); + sprintf(ptr, "| %s", buffer); + ptr += 12; + + /* firmware slot and commit action*/ + sprintf(ptr, "| %d", entry->slot); + ptr += 9; + + if (entry->commit_action_type <= 3) + sprintf(ptr, "| %s", ca[entry->commit_action_type]); + else + sprintf(ptr, "| xxxb"); + ptr += 9; + + /* result */ + if (entry->result) + sprintf(ptr, "| Fail #%d", entry->result); + else + sprintf(ptr, "| pass"); + + /* replace all null charecters with spaces */ + ptr = formatted_entry; + while (index < entry_size) { + if (ptr[index] == '\0') + ptr[index] = ' '; + index++; + } + return 0; } +static void micron_fw_activation_history_header_print(void) +{ + /* header to be printed field widths = 10 | 12 | 10 | 11 | 12 | 9 | 9 | 9 */ + printf("__________________________________________________________________________________\n"); + printf(" | | | | | | |\n"); + printf("Firmware | Power On | Power | Previous | New FW | Slot | Commit | Result\n"); + printf("Activation| Hour | cycle | firmware | activated | number | Action |\n"); + printf("Counter | | count | | | | Type |\n"); + printf("__________|___________|_________|__________|___________|________|________|________\n"); +} static int micron_fw_activation_history(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - const char *desc = "Retrieve Firmware Activation history of the given drive"; - char formatted_output[100]; - int count = 0; - unsigned int logC2[C2_log_size/sizeof(int)] = { 0 }; - eDriveModel eModel = UNKNOWN_MODEL; - struct nvme_dev *dev; - struct format { - char *fmt; - }; - int err; - - const char *fmt = "output format normal"; - struct format cfg = { - .fmt = "normal", - }; - - OPT_ARGS(opts) = { - OPT_FMT("format", 'f', &cfg.fmt, fmt), - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); - if (err < 0) - return -1; - - if (strcmp(cfg.fmt, "normal") != 0) { - fprintf (stderr, "only normal format is supported currently\n"); - dev_close(dev); - return -1; - } - - /* check if product supports fw_history log */ - err = -EINVAL; - if (eModel != M51CX) { - fprintf(stderr, "Unsupported drive model for vs-fw-activate-history command\n"); - goto out; - } - - err = nvme_get_log_simple(dev_fd(dev), 0xC2, C2_log_size, logC2); - if (err) { - fprintf(stderr, "Failed to retrieve fw activation history log, error: %x\n", err); - goto out; - } - - /* check if we have atleast one entry to print */ - struct micron_fw_activation_history_table *table = - (struct micron_fw_activation_history_table *)logC2; - - /* check version and log page */ - if (table->log_page != 0xC2 || (table->version != 2 && table->version != 1)) - { - fprintf(stderr, "Unsupported fw activation history page: %x, version: %x\n", - table->log_page, table->version); - goto out; - } - - if (table->num_entries == 0) { - fprintf(stderr, "No entries were found in fw activation history log\n"); - goto out; - } - - printf("%s", fw_activation_history_table_header); - for(count = 0; count < table->num_entries; count++) { - memset(formatted_output, '\0', 100); - if (display_fw_activate_entry(count, - &table->entries[count], - formatted_output, NULL) == 0) - { - printf("%s\n", formatted_output); - } - } + const char *desc = "Retrieve Firmware Activation history of the given drive"; + char formatted_output[100]; + int count = 0; + unsigned int logC2[C2_log_size/sizeof(int)] = { 0 }; + enum eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_dev *dev; + struct format { + char *fmt; + }; + int err; + + const char *fmt = "output format normal"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); + if (err < 0) + return -1; + + if (strcmp(cfg.fmt, "normal")) { + fprintf(stderr, "only normal format is supported currently\n"); + dev_close(dev); + return -1; + } + + /* check if product supports fw_history log */ + err = -EINVAL; + if (eModel != M51CX) { + fprintf(stderr, "Unsupported drive model for vs-fw-activate-history command\n"); + goto out; + } + + err = nvme_get_log_simple(dev_fd(dev), 0xC2, C2_log_size, logC2); + if (err) { + fprintf(stderr, "Failed to retrieve fw activation history log, error: %x\n", err); + goto out; + } + + /* check if we have atleast one entry to print */ + struct micron_fw_activation_history_table *table = + (struct micron_fw_activation_history_table *)logC2; + + /* check version and log page */ + if (table->log_page != 0xC2 || (table->version != 2 && table->version != 1)) { + fprintf(stderr, "Unsupported fw activation history page: %x, version: %x\n", + table->log_page, table->version); + goto out; + } + + if (!table->num_entries) { + fprintf(stderr, "No entries were found in fw activation history log\n"); + goto out; + } + + micron_fw_activation_history_header_print(); + for (count = 0; count < table->num_entries; count++) { + memset(formatted_output, '\0', 100); + if (!display_fw_activate_entry(count, &table->entries[count], formatted_output, + NULL)) + printf("%s\n", formatted_output); + } out: - dev_close(dev); - return err; + dev_close(dev); + return err; } #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) + struct plugin *plugin) { - 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 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 nvme_dev *dev; - 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() - }; - - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 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); - dev_close(dev); - return -1; - } - - struct nvme_get_features_args g_args = { - .args_size = sizeof(g_args), - .fd = dev_fd(dev), - .fid = fid, - .nsid = 0, - .sel = 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 fid = MICRON_FID_LATENCY_MONITOR; + enum 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 nvme_dev *dev; + 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() + }; + + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 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); + dev_close(dev); + return -1; + } + + struct nvme_get_features_args g_args = { + .args_size = sizeof(g_args), + .fd = dev_fd(dev), + .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"); - dev_close(dev); - 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"); - } - dev_close(dev); - 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"); - dev_close(dev); - 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"); - dev_close(dev); - 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, "trim")) { - command_mask = 0x4; - timing_mask = (opt.threshold << 8); - } else if (strcmp(opt.command, "all")) { - printf("Invalid command %s specified for option %s\n", + err = nvme_get_features(&g_args); + if (err) { + printf("Failed to retrieve latency monitoring feature status\n"); + dev_close(dev); + 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) { + printf("\n"); + } + dev_close(dev); + 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"); + dev_close(dev); + return -1; + } else if (opt.threshold % 10) { + /* timing mask is in terms of 10ms units, so min allowed is 10ms */ + printf("The threshold value should be multiple of 10 ms\n"); + dev_close(dev); + 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, "trim")) { + 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); - dev_close(dev); - return -1; - } - - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .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 %sd latency monitoring for %s commands with %dms threshold\n", - opt.option, opt.command, opt.threshold == 0 ? 800 : opt.threshold * 10); - } else { - printf("Failed to %s latency monitoring for %s commands with %dms threshold\n", - opt.option, opt.command, opt.threshold == 0 ? 800 : opt.threshold * 10); - } - - dev_close(dev); - return err; + dev_close(dev); + return -1; + } + + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .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) { + printf("Successfully %sd latency monitoring for %s commands with %dms threshold\n", + opt.option, opt.command, !opt.threshold ? 800 : opt.threshold * 10); + } else { + printf("Failed to %s latency monitoring for %s commands with %dms threshold\n", + opt.option, opt.command, !opt.threshold ? 800 : opt.threshold * 10); + } + + dev_close(dev); + return err; } static int micron_latency_stats_logs(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { #define LATENCY_LOG_ENTRIES 16 - struct latency_log_entry { - uint64_t timestamp; - uint32_t latency; - uint32_t cmdtag; + struct latency_log_entry { + uint64_t timestamp; + uint32_t latency; + uint32_t cmdtag; union { - struct { - uint32_t opcode:8; + 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 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; + 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]; + enum eDriveModel model = UNKNOWN_MODEL; + struct nvme_dev *dev; + int err = -1; + const char *desc = "Display Latency tracking log information"; + + OPT_ARGS(opts) = { + OPT_END() }; - uint32_t dsm; - uint32_t rfu[6]; - } log[LATENCY_LOG_ENTRIES]; - eDriveModel model = UNKNOWN_MODEL; - struct nvme_dev *dev; - int err = -1; - const char *desc = "Display Latency tracking log information"; - OPT_ARGS(opts) = { - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err) - return err; - memset(&log, 0, sizeof(log)); - err = nvme_get_log_simple(dev_fd(dev), 0xD1, sizeof(log), &log); - if (err) { - if (err < 0) - printf("Unable to retrieve latency stats log the drive\n"); - dev_close(dev); - 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"); - dev_close(dev); - return err; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err) + return err; + memset(&log, 0, sizeof(log)); + err = nvme_get_log_simple(dev_fd(dev), 0xD1, sizeof(log), &log); + if (err) { + if (err < 0) + printf("Unable to retrieve latency stats log the drive\n"); + dev_close(dev); + return err; + } + /* print header and each log entry */ + printf("Timestamp, Latency, CmdTag, Opcode, Fuse, Psdt, Cid, Nsid, Slba_L, Slba_H, Nlb, "); + printf("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"); + dev_close(dev); + return err; } static int micron_latency_stats_info(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + 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; - struct nvme_dev *dev; - eDriveModel model = UNKNOWN_MODEL; - #define LATENCY_BUCKET_COUNT 32 - #define LATENCY_BUCKET_RSVD 32 - struct micron_latency_stats { - uint64_t version; /* major << 32 | minior */ - uint64_t all_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; - uint64_t read_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; - uint64_t write_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; - uint64_t trim_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; - uint32_t reserved[255]; /* 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() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 0) - return err; - if (!strcmp(opt.command, "read")) { - cmd_stats = &log.read_cmds[0]; + const char *desc = "display command latency statistics"; + const char *command = "command to display stats - all|read|write|trimdefault is all"; + int err = 0; + struct nvme_dev *dev; + enum eDriveModel model = UNKNOWN_MODEL; + #define LATENCY_BUCKET_COUNT 32 + #define LATENCY_BUCKET_RSVD 32 + struct micron_latency_stats { + uint64_t version; /* major << 32 | minior */ + uint64_t all_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint64_t read_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint64_t write_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint64_t trim_cmds[LATENCY_BUCKET_COUNT + LATENCY_BUCKET_RSVD]; + uint32_t reserved[255]; /* 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() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 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]; + } 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]; + } 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); - dev_close(dev); + } else if (strcmp(opt.command, "all")) { + printf("Invalid command option %s to display latency stats\n", opt.command); + dev_close(dev); return -1; - } - - memset(&log, 0, sizeof(log)); - err = nvme_get_log_simple(dev_fd(dev), 0xD0, sizeof(log), &log); - if (err) { - if (err < 0) - printf("Unable to retrieve latency stats log the drive\n"); - dev_close(dev); - return err; - } - printf("Micron IO %s Command Latency Statistics\n" + } + + memset(&log, 0, sizeof(log)); + err = nvme_get_log_simple(dev_fd(dev), 0xD0, sizeof(log), &log); + if (err) { + if (err < 0) + printf("Unable to retrieve latency stats log the drive\n"); + dev_close(dev); + 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]); - } - dev_close(dev); - return err; + 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]); + } + dev_close(dev); + return err; } static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - const char *desc = "Retrieve Smart or Extended Smart Health log for the given device "; - unsigned int logC0[C0_log_size/sizeof(int)] = { 0 }; - unsigned int logFB[FB_log_size/sizeof(int)] = { 0 }; - struct nvme_id_ctrl ctrl; - eDriveModel eModel = UNKNOWN_MODEL; - struct nvme_dev *dev; - bool is_json = true; - struct format { - char *fmt; - }; - const char *fmt = "output format normal|json"; - struct format cfg = { - .fmt = "json", - }; - int err = 0; - - OPT_ARGS(opts) = { - OPT_FMT("format", 'f', &cfg.fmt, fmt), - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); - if (err < 0) - return -1; - - if (strcmp(cfg.fmt, "normal") == 0) - is_json = false; - - /* For M5410 and M5407, this option prints 0xFB log page */ - if (eModel == M5410 || eModel == M5407) { - __u8 spec = (eModel == M5410) ? 0 : 1; - __u8 nsze; - - if ((err = nvme_identify_ctrl(dev_fd(dev), &ctrl)) == 0) - err = nvme_get_log_simple(dev_fd(dev), 0xFB, - FB_log_size, logFB); - if (err) { - if (err < 0) - printf("Unable to retrieve smart log 0xFB for the drive\n"); - goto out; - } - - nsze = (ctrl.vs[987] == 0x12); - if (nsze == 0 && nsze_from_oacs) - nsze = ((ctrl.oacs >> 3) & 0x1); - print_nand_stats_fb((__u8 *)logFB, NULL, nsze, is_json, spec); - goto out; - } - - /* check for models that support 0xC0 log */ - if (eModel != M51CX) { - printf ("Unsupported drive model for vs-smart-add-log commmand\n"); - err = -1; - goto out; - } - - err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); - if (err == 0) { - print_smart_cloud_health_log((__u8 *)logC0, is_json); - } else if (err < 0) { - printf("Unable to retrieve extended smart log 0xC0 for the drive\n"); - } + const char *desc = "Retrieve Smart or Extended Smart Health log for the given device "; + unsigned int logC0[C0_log_size/sizeof(int)] = { 0 }; + unsigned int logFB[FB_log_size/sizeof(int)] = { 0 }; + struct nvme_id_ctrl ctrl; + enum eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_dev *dev; + bool is_json = true; + struct format { + char *fmt; + }; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "json", + }; + int err = 0; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &eModel); + if (err < 0) + return -1; + + if (!strcmp(cfg.fmt, "normal")) + is_json = false; + + /* For M5410 and M5407, this option prints 0xFB log page */ + if (eModel == M5410 || eModel == M5407) { + __u8 spec = (eModel == M5410) ? 0 : 1; + __u8 nsze; + + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (!err) + err = nvme_get_log_simple(dev_fd(dev), 0xFB, FB_log_size, logFB); + if (err) { + if (err < 0) + printf("Unable to retrieve smart log 0xFB for the drive\n"); + goto out; + } + + nsze = (ctrl.vs[987] == 0x12); + if (!nsze && nsze_from_oacs) + nsze = ((ctrl.oacs >> 3) & 0x1); + print_nand_stats_fb((__u8 *)logFB, NULL, nsze, is_json, spec); + goto out; + } + + /* check for models that support 0xC0 log */ + if (eModel != M51CX) { + printf("Unsupported drive model for vs-smart-add-log commmand\n"); + err = -1; + goto out; + } + + err = nvme_get_log_simple(dev_fd(dev), 0xC0, C0_log_size, logC0); + if (!err) + print_smart_cloud_health_log((__u8 *)logC0, is_json); + else if (err < 0) + printf("Unable to retrieve extended smart log 0xC0 for the drive\n"); out: - dev_close(dev); - if (err > 0) - nvme_show_status(err); - return err; + dev_close(dev); + if (err > 0) + nvme_show_status(err); + return err; } static int micron_clr_fw_activation_history(int argc, char **argv, - struct command *cmd, struct plugin *plugin) + struct command *cmd, struct plugin *plugin) { - const char *desc = "Clear FW activation history"; - __u32 result = 0; - __u8 fid = MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY; - eDriveModel model = UNKNOWN_MODEL; - struct nvme_dev *dev; - OPT_ARGS(opts) = { - OPT_END() - }; - int err = 0; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 0) - return err; - - if (model != M51CX) { - printf ("This option is not supported for specified drive\n"); - dev_close(dev); - return err; - } - - err = nvme_set_features_simple(dev_fd(dev), fid, 1 << 31, 0, 0, &result); - if (err == 0) err = (int)result; - else printf ("Failed to clear fw activation history, error = 0x%x\n", err); - - dev_close(dev); - return err; + const char *desc = "Clear FW activation history"; + __u32 result = 0; + __u8 fid = MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY; + enum eDriveModel model = UNKNOWN_MODEL; + struct nvme_dev *dev; + + OPT_ARGS(opts) = { + OPT_END() + }; + int err = 0; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 0) + return err; + + if (model != M51CX) { + printf("This option is not supported for specified drive\n"); + dev_close(dev); + return err; + } + + err = nvme_set_features_simple(dev_fd(dev), fid, 1 << 31, 0, 0, &result); + if (!err) + err = (int)result; + else + printf("Failed to clear fw activation history, error = 0x%x\n", err); + + dev_close(dev); + return err; } static int micron_telemetry_cntrl_option(int argc, char **argv, - struct command *cmd, struct plugin *plugin) + struct command *cmd, struct plugin *plugin) { - int err = 0; - __u32 result = 0; - const char *desc = "Enable or Disable Controller telemetry log generation"; - const char *option = "enable or disable or status"; - const char *select = "select/save values: enable/disable options" - "1 - save (persistent), 0 - non-persistent and for " - "status options: 0 - current, 1 - default, 2-saved"; - int fid = MICRON_FEATURE_TELEMETRY_CONTROL_OPTION; - eDriveModel model = UNKNOWN_MODEL; - struct nvme_id_ctrl ctrl = { 0 }; - struct nvme_dev *dev; - - struct { - char *option; - int select; - } opt = { - .option = "disable", - .select= 0, - }; - - OPT_ARGS(opts) = { - OPT_STRING("option", 'o', "option", &opt.option, option), - OPT_UINT("select", 's', &opt.select, select), - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 0) - return -1; - - err = nvme_identify_ctrl(dev_fd(dev), &ctrl); - if ((ctrl.lpa & 0x8) != 0x8) { - printf("drive doesn't support host/controller generated telemetry logs\n"); - dev_close(dev); - return err; - } - - if (!strcmp(opt.option, "enable")) { - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .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")) { - struct nvme_set_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .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")) { - struct nvme_get_features_args args = { - .args_size = sizeof(args), - .fd = dev_fd(dev), - .fid = fid, - .nsid = 1, - .sel = opt.select & 0x3, - .cdw11 = 0, - .uuidx = 0, - .data_len = 0, - .data = NULL, - .timeout = NVME_DEFAULT_IOCTL_TIMEOUT, - .result = &result, + int err = 0; + __u32 result = 0; + const char *desc = "Enable or Disable Controller telemetry log generation"; + const char *option = "enable or disable or status"; + const char *select = + "select/save values: enable/disable options1 - save (persistent), 0 - non-persistent and for status options: 0 - current, 1 - default, 2-saved"; + int fid = MICRON_FEATURE_TELEMETRY_CONTROL_OPTION; + enum eDriveModel model = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl = { 0 }; + struct nvme_dev *dev; + + struct { + char *option; + int select; + } opt = { + .option = "disable", + .select = 0, }; - err = nvme_get_features(&args); - if (err == 0) { - printf("Controller telemetry option : %s\n", - (result) ? "enabled" : "disabled"); - } else { - printf("Failed to retrieve controller telemetry option\n"); - } - } else { - printf("invalid option %s, valid values are enable,disable or status\n", opt.option); - dev_close(dev); - return -1; - } - - dev_close(dev); - return err; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("select", 's', &opt.select, select), + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 0) + return -1; + + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if ((ctrl.lpa & 0x8) != 0x8) { + printf("drive doesn't support host/controller generated telemetry logs\n"); + dev_close(dev); + return err; + } + + if (!strcmp(opt.option, "enable")) { + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .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) + printf("successfully set controller telemetry option\n"); + else + printf("Failed to set controller telemetry option\n"); + } else if (!strcmp(opt.option, "disable")) { + struct nvme_set_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .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) + printf("successfully disabled controller telemetry option\n"); + else + printf("Failed to disable controller telemetry option\n"); + } else if (!strcmp(opt.option, "status")) { + struct nvme_get_features_args args = { + .args_size = sizeof(args), + .fd = dev_fd(dev), + .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) + printf("Controller telemetry option : %s\n", + (result) ? "enabled" : "disabled"); + else + printf("Failed to retrieve controller telemetry option\n"); + } else { + printf("invalid option %s, valid values are enable,disable or status\n", + opt.option); + dev_close(dev); + return -1; + } + + dev_close(dev); + return err; } /* M51XX models log page header */ struct micron_common_log_header { - uint8_t id; - uint8_t version; - uint16_t pn; - uint32_t log_size; - uint32_t max_size; - uint32_t write_pointer; - uint32_t next_pointer; - uint32_t overwritten_bytes; - uint8_t flags; - uint8_t reserved[7]; + uint8_t id; + uint8_t version; + uint16_t pn; + uint32_t log_size; + uint32_t max_size; + uint32_t write_pointer; + uint32_t next_pointer; + uint32_t overwritten_bytes; + uint8_t flags; + uint8_t reserved[7]; }; /* helper function to retrieve logs with specific offset and max chunk size */ int nvme_get_log_lpo(int fd, __u8 log_id, __u32 lpo, __u32 chunk, - __u32 data_len, void *data) + __u32 data_len, void *data) { __u32 offset = lpo, xfer_len = data_len; void *ptr = data; @@ -2933,462 +2920,470 @@ int nvme_get_log_lpo(int fd, __u8 log_id, __u32 lpo, __u32 chunk, /* retrieves logs with common log format */ static int get_common_log(int fd, uint8_t id, uint8_t **buf, int *size) { - struct micron_common_log_header hdr = { 0 }; - int log_size = sizeof(hdr), first = 0, second = 0; - uint8_t *buffer = NULL; - int ret = -1; - int chunk = 0x4000; /* max chunk size to be used for these logs */ - - ret = nvme_get_log_simple(fd, id, sizeof(hdr), &hdr); - if (ret) { - fprintf(stderr, "pull hdr failed for %hhu with error: 0x%x\n", id, ret); - return ret; - } - - if (hdr.id != id || - hdr.log_size == 0 || - hdr.max_size == 0 || - hdr.write_pointer < sizeof(hdr)) - { - fprintf(stderr, "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, " - "max: %u, wp: %u, flags: %hhu, np: %u\n", id, - hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, - hdr.flags, hdr.next_pointer); - return 1; - } - - /* we may have just 32-bytes for some models; write to wfile if log hasn't - * yet reached its max size - */ - if (hdr.log_size == sizeof(hdr)) { - buffer = (uint8_t *)malloc(sizeof(hdr)); - if (buffer == NULL) { - fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n", - sizeof(hdr), id); - return -ENOMEM; - } - memcpy(buffer,(uint8_t *)&hdr, sizeof(hdr)); - } else if (hdr.log_size < hdr.max_size) { - buffer = (uint8_t *)malloc(sizeof(hdr) + hdr.log_size); - if (buffer == NULL) { - fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n", - hdr.log_size + sizeof(hdr), id); - return -ENOMEM; - } - memcpy(buffer, &hdr, sizeof(hdr)); - ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, hdr.log_size, - buffer + sizeof(hdr)); - if (ret == 0) { - log_size += hdr.log_size; - } - } else if (hdr.log_size >= hdr.max_size) { - /* reached maximum, to maintain, sequence we need to depend on write - * pointer to detect wrap-overs. FW doesn't yet implement the condition - * hdr.log_size > hdr.max_size; also ignore over-written log data; we - * also ignore collisions for now - */ - buffer = (uint8_t *)malloc(hdr.max_size + sizeof(hdr)); - if (buffer == NULL) { - fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n", - hdr.max_size + sizeof(hdr), id); - return -ENOMEM; - } - memcpy(buffer, &hdr, sizeof(hdr)); - - first = hdr.max_size - hdr.write_pointer; - second = hdr.write_pointer - sizeof(hdr); - - if (first) { - ret = nvme_get_log_lpo(fd, id, hdr.write_pointer, chunk, first, - buffer + sizeof(hdr)); - if (ret) { - free(buffer); - fprintf(stderr, "failed to get log: 0x%X\n", id); - return ret; - } - log_size += first; - } - if (second) { - ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, second, - buffer + sizeof(hdr) + first); - if (ret) { - fprintf(stderr, "failed to get log: 0x%X\n", id); - free(buffer); + struct micron_common_log_header hdr = { 0 }; + int log_size = sizeof(hdr), first = 0, second = 0; + uint8_t *buffer = NULL; + int ret = -1; + int chunk = 0x4000; /* max chunk size to be used for these logs */ + + ret = nvme_get_log_simple(fd, id, sizeof(hdr), &hdr); + if (ret) { + fprintf(stderr, "pull hdr failed for %u with error: 0x%x\n", id, ret); return ret; - } - log_size += second; } - } - *buf = buffer; - *size = log_size; - return ret; + + if (hdr.id != id || !hdr.log_size || !hdr.max_size || + hdr.write_pointer < sizeof(hdr)) { + fprintf(stderr, + "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, max: %u, wp: %u, flags: %u, np: %u\n", + id, hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, hdr.flags, + hdr.next_pointer); + return 1; + } + + /* + * we may have just 32-bytes for some models; write to wfile if log hasn't + * yet reached its max size + */ + if (hdr.log_size == sizeof(hdr)) { + buffer = (uint8_t *)malloc(sizeof(hdr)); + if (!buffer) { + fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n", + sizeof(hdr), id); + return -ENOMEM; + } + memcpy(buffer, (uint8_t *)&hdr, sizeof(hdr)); + } else if (hdr.log_size < hdr.max_size) { + buffer = (uint8_t *)malloc(sizeof(hdr) + hdr.log_size); + if (!buffer) { + fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n", + hdr.log_size + sizeof(hdr), id); + return -ENOMEM; + } + memcpy(buffer, &hdr, sizeof(hdr)); + ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, hdr.log_size, + buffer + sizeof(hdr)); + if (!ret) + log_size += hdr.log_size; + } else if (hdr.log_size >= hdr.max_size) { + /* + * reached maximum, to maintain, sequence we need to depend on write + * pointer to detect wrap-overs. FW doesn't yet implement the condition + * hdr.log_size > hdr.max_size; also ignore over-written log data; we + * also ignore collisions for now + */ + buffer = (uint8_t *)malloc(hdr.max_size + sizeof(hdr)); + if (!buffer) { + fprintf(stderr, "malloc of %zu bytes failed for log: 0x%X\n", + hdr.max_size + sizeof(hdr), id); + return -ENOMEM; + } + memcpy(buffer, &hdr, sizeof(hdr)); + + first = hdr.max_size - hdr.write_pointer; + second = hdr.write_pointer - sizeof(hdr); + + if (first) { + ret = nvme_get_log_lpo(fd, id, hdr.write_pointer, chunk, first, + buffer + sizeof(hdr)); + if (ret) { + free(buffer); + fprintf(stderr, "failed to get log: 0x%X\n", id); + return ret; + } + log_size += first; + } + if (second) { + ret = nvme_get_log_lpo(fd, id, sizeof(hdr), chunk, second, + buffer + sizeof(hdr) + first); + if (ret) { + fprintf(stderr, "failed to get log: 0x%X\n", id); + free(buffer); + return ret; + } + log_size += second; + } + } + *buf = buffer; + *size = log_size; + return ret; } static int micron_internal_logs(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - int err = -EINVAL; - int ctrlIdx, telemetry_option = 0; - char strOSDirName[1024]; - char strCtrlDirName[1024]; - char strMainDirName[256]; - unsigned int *puiIDDBuf; - unsigned int uiMask; - struct nvme_id_ctrl ctrl; - char sn[20] = { 0 }; - char msg[256] = { 0 }; - int c_logs_index = 8; /* should be current size of aVendorLogs */ - struct nvme_dev *dev; - struct { - unsigned char ucLogPage; - const char *strFileName; - int nLogSize; - int nMaxSize; - } aVendorLogs[32] = { - { 0x03, "firmware_slot_info_log.bin", 512, 0 }, - { 0xC1, "nvmelog_C1.bin", 0, 0 }, - { 0xC2, "nvmelog_C2.bin", 0, 0 }, - { 0xC4, "nvmelog_C4.bin", 0, 0 }, - { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, - { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, - { 0xE6, "nvmelog_E6.bin", 0, 0 }, - { 0xE7, "nvmelog_E7.bin", 0, 0 } - }, - aM51XXLogs[] = { - { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ - { 0xD0, "nvmelog_D0.bin", 512, 0 }, - { 0x03, "firmware_slot_info_log.bin", 512, 0}, - { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, - { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, - { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, - { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, - { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } - }, - aM51AXLogs[] = { - { 0xCA, "nvmelog_CA.bin", 512, 0 }, - { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, - { 0xF6, "nvmelog_F6.bin", 4096, 512 * 1024 }, - { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, - { 0x04, "changed_namespace_log.bin", 4096, 0 }, - { 0x05, "command_effects_log.bin", 4096, 0 }, - { 0x06, "drive_self_test.bin", 4096, 0 } - }, - aM51BXLogs[] = { - { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, - { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, - { 0xCA, "nvmelog_CA.bin", 512, 1024 } - }, - aM51CXLogs[] = { - { 0xE1, "nvmelog_E1.bin", 0, 0 }, - { 0xE2, "nvmelog_E2.bin", 0, 0 }, - { 0xE3, "nvmelog_E3.bin", 0, 0 }, - { 0xE4, "nvmelog_E4.bin", 0, 0 }, - { 0xE5, "nvmelog_E5.bin", 0, 0 }, - { 0xE8, "nvmelog_E8.bin", 0, 0 }, - { 0xE9, "nvmelog_E9.bin", 0, 0 }, - { 0xEA, "nvmelog_EA.bin", 0, 0 }, - }; - - eDriveModel eModel; - - const char *desc = "This retrieves the micron debug log package"; - const char *package = "Log output data file name (required)"; - const char *type = "telemetry log type - host or controller"; - const char *data_area = "telemetry log data area 1, 2 or 3"; - unsigned char *dataBuffer = NULL; - int bSize = 0; - int maxSize = 0; - - struct config { - char *type; - char *package; - int data_area; - int log; - }; - - struct config cfg = { - .type = "", - .package = "", - .data_area = -1, - .log = 0x07, - }; - - OPT_ARGS(opts) = { - OPT_STRING("type", 't', "log type", &cfg.type, type), - OPT_STRING("package", 'p', "FILE", &cfg.package, package), - OPT_UINT("data_area", 'd', &cfg.data_area, data_area), - OPT_END() - }; - - err = parse_and_open(&dev, argc, argv, desc, opts); - if (err) - return err; - - /* if telemetry type is specified, check for data area */ - if (strlen(cfg.type) != 0) { - if (!strcmp(cfg.type, "controller")) { - cfg.log = 0x08; - } else if (strcmp(cfg.type, "host")) { - printf ("telemetry type (host or controller) should be specified i.e. -t=host\n"); - goto out; - } - - if (cfg.data_area <= 0 || cfg.data_area > 3) { - printf ("data area must be selected using -d option ie --d=1,2,3\n"); - goto out; - } - telemetry_option = 1; - } else if (cfg.data_area > 0) { - printf ("data area option is valid only for telemetry option (i.e --type=host|controller)\n"); - goto out; - } - - if (strlen(cfg.package) == 0) { - if (telemetry_option) - printf ("Log data file must be specified. ie -p=logfile.bin\n"); - else - printf ("Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz\n"); - goto out; - } - - /* pull log details based on the model name */ - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { - printf ("Unsupported drive model for vs-internal-log collection\n"); - goto out; - } - - err = nvme_identify_ctrl(dev_fd(dev), &ctrl); - if (err) - goto out; - - err = -EINVAL; - if (telemetry_option) { - if ((ctrl.lpa & 0x8) != 0x8) { - printf("telemetry option is not supported for specified drive\n"); - goto out; - } - int logSize = 0; __u8 *buffer = NULL; const char *dir = "."; - err = micron_telemetry_log(dev_fd(dev), cfg.log, &buffer, &logSize, + int err = -EINVAL; + int ctrlIdx, telemetry_option = 0; + char strOSDirName[1024]; + char strCtrlDirName[1024]; + char strMainDirName[256]; + unsigned int *puiIDDBuf; + unsigned int uiMask; + struct nvme_id_ctrl ctrl; + char sn[20] = { 0 }; + char msg[256] = { 0 }; + int c_logs_index = 8; /* should be current size of aVendorLogs */ + struct nvme_dev *dev; + struct { + unsigned char ucLogPage; + const char *strFileName; + int nLogSize; + int nMaxSize; + } aVendorLogs[32] = { + { 0x03, "firmware_slot_info_log.bin", 512, 0 }, + { 0xC1, "nvmelog_C1.bin", 0, 0 }, + { 0xC2, "nvmelog_C2.bin", 0, 0 }, + { 0xC4, "nvmelog_C4.bin", 0, 0 }, + { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, + { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, + { 0xE6, "nvmelog_E6.bin", 0, 0 }, + { 0xE7, "nvmelog_E7.bin", 0, 0 } + }, + aM51XXLogs[] = { + { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ + { 0xD0, "nvmelog_D0.bin", 512, 0 }, + { 0x03, "firmware_slot_info_log.bin", 512, 0}, + { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, + { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, + { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, + { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, + { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } + }, + aM51AXLogs[] = { + { 0xCA, "nvmelog_CA.bin", 512, 0 }, + { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, + { 0xF6, "nvmelog_F6.bin", 4096, 512 * 1024 }, + { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, + { 0x04, "changed_namespace_log.bin", 4096, 0 }, + { 0x05, "command_effects_log.bin", 4096, 0 }, + { 0x06, "drive_self_test.bin", 4096, 0 } + }, + aM51BXLogs[] = { + { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, + { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, + { 0xCA, "nvmelog_CA.bin", 512, 1024 } + }, + aM51CXLogs[] = { + { 0xE1, "nvmelog_E1.bin", 0, 0 }, + { 0xE2, "nvmelog_E2.bin", 0, 0 }, + { 0xE3, "nvmelog_E3.bin", 0, 0 }, + { 0xE4, "nvmelog_E4.bin", 0, 0 }, + { 0xE5, "nvmelog_E5.bin", 0, 0 }, + { 0xE8, "nvmelog_E8.bin", 0, 0 }, + { 0xE9, "nvmelog_E9.bin", 0, 0 }, + { 0xEA, "nvmelog_EA.bin", 0, 0 }, + }; + + enum eDriveModel eModel; + + const char *desc = "This retrieves the micron debug log package"; + const char *package = "Log output data file name (required)"; + const char *type = "telemetry log type - host or controller"; + const char *data_area = "telemetry log data area 1, 2 or 3"; + unsigned char *dataBuffer = NULL; + int bSize = 0; + int maxSize = 0; + + struct config { + char *type; + char *package; + int data_area; + int log; + }; + + struct config cfg = { + .type = "", + .package = "", + .data_area = -1, + .log = 0x07, + }; + + OPT_ARGS(opts) = { + OPT_STRING("type", 't', "log type", &cfg.type, type), + OPT_STRING("package", 'p', "FILE", &cfg.package, package), + OPT_UINT("data_area", 'd', &cfg.data_area, data_area), + OPT_END() + }; + + err = parse_and_open(&dev, argc, argv, desc, opts); + if (err) + return err; + + /* if telemetry type is specified, check for data area */ + if (strlen(cfg.type)) { + if (!strcmp(cfg.type, "controller")) { + cfg.log = 0x08; + } else if (strcmp(cfg.type, "host")) { + printf("telemetry type (host or controller) should be specified i.e. -t=host\n"); + goto out; + } + + if (cfg.data_area <= 0 || cfg.data_area > 3) { + printf("data area must be selected using -d option ie --d=1,2,3\n"); + goto out; + } + telemetry_option = 1; + } else if (cfg.data_area > 0) { + printf("data area option is valid only for telemetry option (i.e --type=host|controller)\n"); + goto out; + } + + if (!strlen(cfg.package)) { + if (telemetry_option) + printf("Log data file must be specified. ie -p=logfile.bin\n"); + else + printf("Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz\n"); + goto out; + } + + /* pull log details based on the model name */ + if (sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx) != 1) + ctrlIdx = 0; + eModel = GetDriveModel(ctrlIdx); + if (eModel == UNKNOWN_MODEL) { + printf("Unsupported drive model for vs-internal-log collection\n"); + goto out; + } + + err = nvme_identify_ctrl(dev_fd(dev), &ctrl); + if (err) + goto out; + + err = -EINVAL; + if (telemetry_option) { + if ((ctrl.lpa & 0x8) != 0x8) { + printf("telemetry option is not supported for specified drive\n"); + goto out; + } + int logSize = 0; __u8 *buffer = NULL; const char *dir = "."; + + err = micron_telemetry_log(dev_fd(dev), 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); - free(buffer); - } - goto out; - } - - printf("Preparing log package. This will take a few seconds...\n"); - - /* trim spaces out of serial number string */ - int i, j = 0; - for (i = 0; i < sizeof(ctrl.sn); i++) { - if (isblank((int)ctrl.sn[i])) - continue; - sn[j++] = ctrl.sn[i]; - } - sn[j] = '\0'; - strcpy(ctrl.sn, sn); - - SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); - - GetTimestampInfo(strOSDirName); - GetCtrlIDDInfo(strCtrlDirName, &ctrl); - GetOSConfig(strOSDirName); - GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); - - for (int i = 1; i <= ctrl.nn; i++) - GetNSIDDInfo(dev_fd(dev), strCtrlDirName, i); - - GetSmartlogData(dev_fd(dev), strCtrlDirName); - GetErrorlogData(dev_fd(dev), ctrl.elpe, strCtrlDirName); - GetGenericLogs(dev_fd(dev), strCtrlDirName); - /* pull if telemetry log data is supported */ - if ((ctrl.lpa & 0x8) == 0x8) - GetTelemetryData(dev_fd(dev), strCtrlDirName); - - GetFeatureSettings(dev_fd(dev), strCtrlDirName); - - if (eModel != M5410 && eModel != M5407) { - memcpy(&aVendorLogs[c_logs_index], aM51XXLogs, sizeof(aM51XXLogs)); - c_logs_index += sizeof(aM51XXLogs)/sizeof(aM51XXLogs[0]); - if (eModel == M51AX) - memcpy((char *)&aVendorLogs[c_logs_index], aM51AXLogs, sizeof(aM51AXLogs)); - else if (eModel == M51BX) - memcpy((char *)&aVendorLogs[c_logs_index], aM51BXLogs, sizeof(aM51BXLogs)); - else if (eModel == M51CX) - memcpy((char *)&aVendorLogs[c_logs_index], aM51CXLogs, sizeof(aM51CXLogs)); - } - - for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && - aVendorLogs[i].ucLogPage != 0; i++) { - err = -1; - switch (aVendorLogs[i].ucLogPage) { - case 0xE1: - case 0xE5: - case 0xE9: - err = 1; - break; - - case 0xE2: - case 0xE3: - case 0xE4: - case 0xE8: - case 0xEA: - err = get_common_log(dev_fd(dev), aVendorLogs[i].ucLogPage, + if (!err && logSize > 0 && buffer) { + sprintf(msg, "telemetry log: 0x%X", cfg.log); + WriteData(buffer, logSize, dir, cfg.package, msg); + free(buffer); + } + goto out; + } + + printf("Preparing log package. This will take a few seconds...\n"); + + /* trim spaces out of serial number string */ + int i, j = 0; + + for (i = 0; i < sizeof(ctrl.sn); i++) { + if (isblank((int)ctrl.sn[i])) + continue; + sn[j++] = ctrl.sn[i]; + } + sn[j] = '\0'; + strcpy(ctrl.sn, sn); + + SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); + + GetTimestampInfo(strOSDirName); + GetCtrlIDDInfo(strCtrlDirName, &ctrl); + GetOSConfig(strOSDirName); + GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); + + for (int i = 1; i <= ctrl.nn; i++) + GetNSIDDInfo(dev_fd(dev), strCtrlDirName, i); + + GetSmartlogData(dev_fd(dev), strCtrlDirName); + GetErrorlogData(dev_fd(dev), ctrl.elpe, strCtrlDirName); + GetGenericLogs(dev_fd(dev), strCtrlDirName); + /* pull if telemetry log data is supported */ + if ((ctrl.lpa & 0x8) == 0x8) + GetTelemetryData(dev_fd(dev), strCtrlDirName); + + GetFeatureSettings(dev_fd(dev), strCtrlDirName); + + if (eModel != M5410 && eModel != M5407) { + memcpy(&aVendorLogs[c_logs_index], aM51XXLogs, sizeof(aM51XXLogs)); + c_logs_index += ARRAY_SIZE(aM51XXLogs); + if (eModel == M51AX) + memcpy((char *)&aVendorLogs[c_logs_index], aM51AXLogs, sizeof(aM51AXLogs)); + else if (eModel == M51BX) + memcpy((char *)&aVendorLogs[c_logs_index], aM51BXLogs, sizeof(aM51BXLogs)); + else if (eModel == M51CX) + memcpy((char *)&aVendorLogs[c_logs_index], aM51CXLogs, sizeof(aM51CXLogs)); + } + + for (int i = 0; i < (int)(ARRAY_SIZE(aVendorLogs)) && aVendorLogs[i].ucLogPage; i++) { + err = -1; + switch (aVendorLogs[i].ucLogPage) { + case 0xE1: + fallthrough; + case 0xE5: + fallthrough; + case 0xE9: + err = 1; + break; + case 0xE2: + fallthrough; + case 0xE3: + fallthrough; + case 0xE4: + fallthrough; + case 0xE8: + fallthrough; + case 0xEA: + err = get_common_log(dev_fd(dev), aVendorLogs[i].ucLogPage, &dataBuffer, &bSize); - break; - - case 0xC1: - case 0xC2: - case 0xC4: - err = GetLogPageSize(dev_fd(dev), aVendorLogs[i].ucLogPage, - &bSize); - if (err == 0 && bSize > 0) - err = GetCommonLogPage(dev_fd(dev), aVendorLogs[i].ucLogPage, - &dataBuffer, bSize); - break; - - case 0xE6: - case 0xE7: - puiIDDBuf = (unsigned int *)&ctrl; - uiMask = puiIDDBuf[1015]; - if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || - (aVendorLogs[i].ucLogPage == 0xE7 && uiMask == 1)) { - bSize = 0; - } else { - bSize = (int)puiIDDBuf[1023]; - if (bSize % (16 * 1024)) { - bSize += (16 * 1024) - (bSize % (16 * 1024)); - } - } - if (bSize != 0 && (dataBuffer = (unsigned char *)malloc(bSize)) != NULL) { - memset(dataBuffer, 0, bSize); - if (eModel == M5410 || eModel == M5407) - err = NVMEGetLogPage(dev_fd(dev), - aVendorLogs[i].ucLogPage, dataBuffer, - bSize); - else - err = nvme_get_log_simple(dev_fd(dev), - aVendorLogs[i].ucLogPage, - bSize, dataBuffer); - } - break; - - case 0xF7: - case 0xF9: - case 0xFC: - case 0xFD: - if (eModel == M51BX) { - (void)NVMEResetLog(dev_fd(dev), aVendorLogs[i].ucLogPage, - aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); - } - /* fallthrough */ - default: - bSize = aVendorLogs[i].nLogSize; - dataBuffer = (unsigned char *)malloc(bSize); - if (dataBuffer == NULL) { - break; - } - memset(dataBuffer, 0, bSize); - err = nvme_get_log_simple(dev_fd(dev), 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_simple(dev_fd(dev), + break; + case 0xC1: + fallthrough; + case 0xC2: + fallthrough; + case 0xC4: + err = GetLogPageSize(dev_fd(dev), aVendorLogs[i].ucLogPage, + &bSize); + if (!err && bSize > 0) + err = GetCommonLogPage(dev_fd(dev), aVendorLogs[i].ucLogPage, + &dataBuffer, bSize); + break; + case 0xE6: + fallthrough; + case 0xE7: + puiIDDBuf = (unsigned int *)&ctrl; + uiMask = puiIDDBuf[1015]; + if (!uiMask || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || + (aVendorLogs[i].ucLogPage == 0xE7 && uiMask == 1)) { + bSize = 0; + } else { + bSize = (int)puiIDDBuf[1023]; + if (bSize % (16 * 1024)) + bSize += (16 * 1024) - (bSize % (16 * 1024)); + } + dataBuffer = (unsigned char *)malloc(bSize); + if (bSize && dataBuffer) { + memset(dataBuffer, 0, bSize); + if (eModel == M5410 || eModel == M5407) + err = NVMEGetLogPage(dev_fd(dev), + aVendorLogs[i].ucLogPage, dataBuffer, + bSize); + else + err = nvme_get_log_simple(dev_fd(dev), + aVendorLogs[i].ucLogPage, + bSize, dataBuffer); + } + break; + case 0xF7: + fallthrough; + case 0xF9: + fallthrough; + case 0xFC: + fallthrough; + case 0xFD: + if (eModel == M51BX) + (void)NVMEResetLog(dev_fd(dev), aVendorLogs[i].ucLogPage, + aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); + fallthrough; + default: + bSize = aVendorLogs[i].nLogSize; + dataBuffer = (unsigned char *)malloc(bSize); + if (!dataBuffer) + break; + memset(dataBuffer, 0, bSize); + err = nvme_get_log_simple(dev_fd(dev), aVendorLogs[i].ucLogPage, + bSize, dataBuffer); + maxSize = aVendorLogs[i].nMaxSize - bSize; + while (!err && 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_simple(dev_fd(dev), aVendorLogs[i].ucLogPage, bSize, dataBuffer); - if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) - break; - maxSize -= bSize; - } - break; - } - - if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { - sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); - WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); - } - - if (dataBuffer != NULL) { - free(dataBuffer); - dataBuffer = NULL; - } - } - - err = ZipAndRemoveDir(strMainDirName, cfg.package); + if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) + break; + maxSize -= bSize; + } + break; + } + + if (!err && dataBuffer && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { + sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); + WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); + } + + if (dataBuffer) { + free(dataBuffer); + dataBuffer = NULL; + } + } + + err = ZipAndRemoveDir(strMainDirName, cfg.package); out: - dev_close(dev); - return err; + dev_close(dev); + return err; } #define MIN_LOG_SIZE 512 static int micron_logpage_dir(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - int err = -1; - const char *desc = "List the supported log pages"; - eDriveModel model = UNKNOWN_MODEL; - char logbuf[MIN_LOG_SIZE]; - struct nvme_dev *dev; - int i; - - OPT_ARGS(opts) = { - OPT_END() - }; - - err = micron_parse_options(&dev, argc, argv, desc, opts, &model); - if (err < 0) - return err; - - struct nvme_supported_logs { - uint8_t log_id; - uint8_t supported; - char *desc; - } log_list[] = { - {0x00, 0, "Support Log Pages"}, - {0x01, 0, "Error Information"}, - {0x02, 0, "SMART / Health Information"}, - {0x03, 0, "Firmware Slot Information"}, - {0x04, 0, "Changed Namespace List"}, - {0x05, 0, "Commands Supported and Effects"}, - {0x06, 0, "Device Self Test"}, - {0x07, 0, "Telemetry Host-Initiated"}, - {0x08, 0, "Telemetry Controller-Initiated"}, - {0x09, 0, "Endurance Group Information"}, - {0x0A, 0, "Predictable Latency Per NVM Set"}, - {0x0B, 0, "Predictable Latency Event Aggregate"}, - {0x0C, 0, "Asymmetric Namespace Access"}, - {0x0D, 0, "Persistent Event Log"}, - {0x0E, 0, "Predictable Latency Event Aggregate"}, - {0x0F, 0, "Endurance Group Event Aggregate"}, - {0x10, 0, "Media Unit Status"}, - {0x11, 0, "Supported Capacity Configuration List"}, - {0x12, 0, "Feature Identifiers Supported and Effects"}, - {0x13, 0, "NVMe-MI Commands Supported and Effects"}, - {0x14, 0, "Command and Feature lockdown"}, - {0x15, 0, "Boot Partition"}, - {0x16, 0, "Rotational Media Information"}, - {0x70, 0, "Discovery"}, - {0x80, 0, "Reservation Notification"}, - {0x81, 0, "Sanitize Status"}, - {0xC0, 0, "SMART Cloud Health Log"}, - {0xC2, 0, "Firmware Activation History"}, - {0xC3, 0, "Latency Monitor Log"}, - }; - - printf("Supported log page list\nLog ID : Description\n"); - for (i = 0; i < sizeof(log_list)/sizeof(log_list[0]); i++) { - err = nvme_get_log_simple(dev_fd(dev), log_list[i].log_id, - MIN_LOG_SIZE, &logbuf[0]); - if (err) continue; - printf("%02Xh : %s\n", log_list[i].log_id, log_list[i].desc); - } - - return err; + int err = -1; + const char *desc = "List the supported log pages"; + enum eDriveModel model = UNKNOWN_MODEL; + char logbuf[MIN_LOG_SIZE]; + struct nvme_dev *dev; + int i; + + OPT_ARGS(opts) = { + OPT_END() + }; + + err = micron_parse_options(&dev, argc, argv, desc, opts, &model); + if (err < 0) + return err; + + struct nvme_supported_logs { + uint8_t log_id; + uint8_t supported; + char *desc; + } log_list[] = { + {0x00, 0, "Support Log Pages"}, + {0x01, 0, "Error Information"}, + {0x02, 0, "SMART / Health Information"}, + {0x03, 0, "Firmware Slot Information"}, + {0x04, 0, "Changed Namespace List"}, + {0x05, 0, "Commands Supported and Effects"}, + {0x06, 0, "Device Self Test"}, + {0x07, 0, "Telemetry Host-Initiated"}, + {0x08, 0, "Telemetry Controller-Initiated"}, + {0x09, 0, "Endurance Group Information"}, + {0x0A, 0, "Predictable Latency Per NVM Set"}, + {0x0B, 0, "Predictable Latency Event Aggregate"}, + {0x0C, 0, "Asymmetric Namespace Access"}, + {0x0D, 0, "Persistent Event Log"}, + {0x0E, 0, "Predictable Latency Event Aggregate"}, + {0x0F, 0, "Endurance Group Event Aggregate"}, + {0x10, 0, "Media Unit Status"}, + {0x11, 0, "Supported Capacity Configuration List"}, + {0x12, 0, "Feature Identifiers Supported and Effects"}, + {0x13, 0, "NVMe-MI Commands Supported and Effects"}, + {0x14, 0, "Command and Feature lockdown"}, + {0x15, 0, "Boot Partition"}, + {0x16, 0, "Rotational Media Information"}, + {0x70, 0, "Discovery"}, + {0x80, 0, "Reservation Notification"}, + {0x81, 0, "Sanitize Status"}, + {0xC0, 0, "SMART Cloud Health Log"}, + {0xC2, 0, "Firmware Activation History"}, + {0xC3, 0, "Latency Monitor Log"}, + }; + + printf("Supported log page list\nLog ID : Description\n"); + for (i = 0; i < ARRAY_SIZE(log_list); i++) { + err = nvme_get_log_simple(dev_fd(dev), log_list[i].log_id, + MIN_LOG_SIZE, &logbuf[0]); + if (err) + continue; + printf("%02Xh : %s\n", log_list[i].log_id, log_list[i].desc); + } + + return err; } |