Bug Summary

File:.build-ci/../plugins/micron/micron-nvme.c
Warning:line 243, column 8
An undefined value may be read from 'errno'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name micron-nvme.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/lib/llvm-19/lib/clang/19 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -std=gnu99 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-06-24-175442-590-1 -x c ../plugins/micron/micron-nvme.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) Micron, Inc 2024.
4 *
5 * @file: micron-nvme.c
6 * @brief: This module contains all the constructs needed for micron nvme-cli plugin.
7 * @authors:Hanumanthu H <hanumanthuh@micron.com>
8 * Chaithanya Shoba <ashoba@micron.com>
9 * Sivaprasad Gutha <sivaprasadg@micron.com>
10 */
11
12#include <ctype.h>
13#include <errno(*__errno_location ()).h>
14#include <fcntl.h>
15#include <inttypes.h>
16#include <libgen.h>
17#include <limits.h>
18#include <stddef.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <time.h>
23#include <unistd.h>
24
25#include <sys/stat.h>
26#include <sys/types.h>
27#include <dirent.h>
28
29#include <libnvme.h>
30
31#include "common.h"
32#include "nvme-cmds.h"
33#include "nvme-print.h"
34#include "nvme.h"
35#include "util/cleanup.h"
36#include "util/types.h"
37#include "util/utils.h"
38
39#define CREATE_CMD
40#include "micron-nvme.h"
41#include "micron-utils.h"
42
43/* Supported Vendor specific feature ids */
44#define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS0xC3 0xC3
45#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY0xC1 0xC1
46#define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION0xCF 0xCF
47#define MICRON_FEATURE_SMBUS_OPTION0xD5 0xD5
48#define MICRON_FEATURE_OCP_ENHANCED_TELEMETRY0x16 0x16
49
50/* Micron Supported Customer ID*/
51#define MICRON_CUST_ID_GENERAL0x10 0x10
52#define MICRON_CUST_ID_GG0x16 0x16
53
54/* Supported Vendor specific log page sizes */
55#define C5_log_size(((452 + 16 * 1024) / 4) * 4096) (((452 + 16 * 1024) / 4) * 4096)
56#define C0_log_size512 512
57#define C2_log_size4096 4096
58#define D0_log_size512 512
59#define FB_log_size512 512
60#define E1_log_size256 256
61#define MaxLogChunk(16 * 1024) (16 * 1024)
62#define CommonChunkSize(16 * 4096) (16 * 4096)
63#define C6_log_size512 512
64#define C5_MicronWorkLoad_log_size256 256
65
66#define SensorCount8 8
67
68/* Plugin version major_number.minor_number.patch */
69static const char *__version_major = "2";
70static const char *__version_minor = "1";
71static const char *__version_patch = "0";
72
73/*
74 * supported models of micron plugin; new models should be added at the end
75 * before UNKNOWN_MODEL. Make sure M5410 is first in the list !
76 */
77enum eDriveModel {
78 M5410 = 0,
79 M51AX,
80 M51BX,
81 M51BY,
82 M51CY,
83 M51CX,
84 M5407,
85 M5411,
86 M6001,
87 M6003,
88 M6004,
89 UNKNOWN_MODEL
90};
91
92#define MICRON_VENDOR_ID0x1344 0x1344
93
94static unsigned short vendor_id;
95static unsigned short device_id;
96
97/* Additional log page IDs */
98#define NVME_LOG_PERSISTENT_EVENT0xD 0xD
99
100struct LogPageHeader_t {
101 unsigned char numDwordsInLogPageHeaderLo;
102 unsigned char logPageHeaderFormatVersion;
103 unsigned char logPageId;
104 unsigned char numDwordsInLogPageHeaderHi;
105 unsigned int numValidDwordsInPayload;
106 unsigned int numDwordsInEntireLogPage;
107};
108
109struct MICRON_WORKLOAD_LOG_HDR {
110 unsigned short usNumEntries;
111 unsigned short usVersion;
112 unsigned int uiLength;
113};
114
115static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg)
116{
117 char tempFolder[8192] = { 0 };
118 FILE *fpOutFile = NULL((void*)0);
119
120 sprintf(tempFolder, "%s/%s", dir, file);
121 fpOutFile = fopen(tempFolder, "ab+");
122 if (fpOutFile) {
123 if (fwrite(data, 1, len, fpOutFile) != len)
124 printf("Failed to write %s data to %s\n", msg, tempFolder);
125 fclose(fpOutFile);
126 } else {
127 printf("Failed to open %s file to write %s\n", tempFolder, msg);
128 }
129}
130
131static enum eDriveModel GetDriveModel(
132 struct libnvme_global_ctx *ctx,
133 struct libnvme_transport_handle *hdl)
134{
135 enum eDriveModel eModel = UNKNOWN_MODEL;
136
137 micron_get_pci_ids(ctx, hdl, &vendor_id, &device_id);
138
139 if (vendor_id == MICRON_VENDOR_ID0x1344) {
140 switch (device_id) {
141 case 0x5196:
142 case 0x51A0:
143 case 0x51A1:
144 case 0x51A2:
145 eModel = M51AX;
146 break;
147 case 0x51B0:
148 case 0x51B1:
149 case 0x51B2:
150 eModel = M51BX;
151 break;
152 case 0x51B7:
153 case 0x51B8:
154 case 0x51B9:
155 eModel = M51BY;
156 break;
157 case 0x51BB:
158 case 0x51BD:
159 case 0x51BC:
160 case 0x51BE:
161 case 0x51BF:
162 case 0x51C8:
163 case 0x51C9:
164 case 0x51CA:
165 case 0x51CB:
166 case 0x51CC:
167 case 0x51CD:
168 case 0x51CE:
169 eModel = M51CY;
170 break;
171 case 0x51C0:
172 case 0x51C1:
173 case 0x51C2:
174 case 0x51C3:
175 case 0x51C4:
176 eModel = M51CX;
177 break;
178 case 0x5405:
179 case 0x5406:
180 case 0x5407:
181 eModel = M5407;
182 break;
183 case 0x5410:
184 eModel = M5410;
185 break;
186 case 0x5411:
187 eModel = M5411;
188 break;
189 case 0x6001:
190 eModel = M6001;
191 break;
192 case 0x6004:
193 eModel = M6004;
194 break;
195 case 0x6003:
196 eModel = M6003;
197 break;
198 default:
199 break;
200 }
201 }
202 return eModel;
203}
204
205/*
206 * Recursively remove a directory and its contents.
207 * Since this is only used for temporary directories that we create that
208 * have no symlinks, it is safe to not check for and handle symlinks here.
209 */
210static int RemoveDirRecursive(const char *path)
211{
212 DIR *dir = NULL((void*)0);
213 struct dirent *entry;
214 char child[PATH_MAX4096];
215
216 dir = opendir(path);
217 if (!dir
4.1
'dir' is non-null
12.1
'dir' is non-null
) {
5
Taking false branch
13
Taking false branch
218 if (errno(*__errno_location ()) == ENOENT2)
219 return 0;
220 return -1;
221 }
222
223 while ((entry = readdir(dir))) {
224 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
6
Assuming the condition is false
7
Assuming the condition is false
8
Taking false branch
14
Assuming the condition is false
15
Assuming the condition is false
16
Taking false branch
225 continue;
226
227 if (snprintf(child, sizeof(child), "%s/%s", path, entry->d_name) >=
9
Assuming the condition is false
17
Assuming the condition is true
18
Taking true branch
228 (int)sizeof(child)) {
229 errno(*__errno_location ()) = ENAMETOOLONG36;
230 closedir(dir);
19
Assuming that 'closedir' is successful; 'errno' becomes undefined after the call
231 return -1;
232 }
233
234 if (unlink(child) == 0 || errno(*__errno_location ()) == ENOENT2)
10
Assuming the condition is false
235 continue;
236
237 if (errno(*__errno_location ()) != EISDIR21 && errno(*__errno_location ()) != EPERM1) {
11
Assuming the condition is false
238 closedir(dir);
239 return -1;
240 }
241
242 if (RemoveDirRecursive(child) < 0) {
12
Calling 'RemoveDirRecursive'
20
Returning from 'RemoveDirRecursive'
21
Taking true branch
243 if (errno(*__errno_location ()) == ENOENT2)
22
An undefined value may be read from 'errno'
244 continue;
245 closedir(dir);
246 return -1;
247 }
248 }
249
250 closedir(dir);
251
252 if (rmdir(path) < 0 && errno(*__errno_location ()) != ENOENT2)
253 return -1;
254
255 return 0;
256}
257
258/*
259 * bsdtar-based versions of tar support creating zip archives when -a is used
260 * with a .zip extension. Check if bsdtar is available and use it to create the
261 * requested zip archive.
262 *
263 * Returns 0 on success, or a negative errno value if tar is not bsdtar
264 * or if the command fails.
265 */
266static int ZipWithBsdTar(char *strDirName, char *strFileName)
267{
268 __cleanup_free__attribute__((cleanup(freep))) char *cmd_buf = NULL((void*)0);
269 FILE *fpVersion = NULL((void*)0);
270 char version_buf[256] = { 0 };
271 bool_Bool is_bsdtar = false0;
272
273 fpVersion = popen("tar --version 2>&1", "r");
274 if (!fpVersion)
275 return -EINVAL22;
276
277 while (fgets(version_buf, sizeof(version_buf), fpVersion)) {
278 if (strstr(version_buf, "bsdtar")) {
279 is_bsdtar = true1;
280 break;
281 }
282 }
283
284 if (pclose(fpVersion))
285 return -EINVAL22;
286 fpVersion = NULL((void*)0);
287
288 if (!is_bsdtar)
289 return -EINVAL22;
290
291 if (asprintf(&cmd_buf, "tar -caf \"%s\" \"%s\"",
292 strFileName, strDirName) < 0)
293 return -ENOMEM12;
294
295 if (system(cmd_buf))
296 return -EIO5;
297 return 0;
298}
299
300static int ZipAndRemoveDir(char *strDirName, char *strFileName)
301{
302 int err = 0;
303 char strBuffer[PATH_MAX4096 + 128]; /* cmd + path */
304 int nRet;
305 bool_Bool is_tgz = false0;
306 struct stat sb;
307
308 if (strstr(strFileName, ".tar.gz") || strstr(strFileName, ".tgz")) {
1
Assuming the condition is true
309 snprintf(strBuffer, sizeof(strBuffer), "tar -zcf \"%s\" \"%s\"", strFileName, strDirName);
310 is_tgz = true1;
311 } else {
312 snprintf(strBuffer, sizeof(strBuffer), "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName,
313 strDirName);
314 }
315
316 nRet = system(strBuffer);
317 if (nRet && !is_tgz
2.1
'is_tgz' is true
)
2
Assuming 'nRet' is not equal to 0
318 /* if zip is not available, see if tar can be used instead */
319 nRet = ZipWithBsdTar(strDirName, strFileName);
320
321 /* check if log file is created, if not print error message */
322 if (nRet
2.2
'nRet' is not equal to 0
|| (stat(strFileName, &sb) == -1)) {
323 err = -EINVAL22;
324 if (is_tgz
2.3
'is_tgz' is true
)
3
Taking true branch
325 snprintf(strBuffer, sizeof(strBuffer), "check if tar and gzip commands are installed");
326 else
327 snprintf(strBuffer, sizeof(strBuffer), "check if zip command is installed");
328
329 fprintf(stderrstderr, "Failed to create log data package, %s!\n", strBuffer);
330 }
331
332 if (RemoveDirRecursive(strDirName) < 0)
4
Calling 'RemoveDirRecursive'
333 printf("Failed to remove temporary files!\n");
334
335 if (unlink("temp.txt") < 0 && errno(*__errno_location ()) != ENOENT2)
336 printf("Failed to remove temporary file temp.txt!\n");
337 return err;
338}
339
340static int SetupDebugDataDirectories(char *strSN, char *strFilePath,
341 char *strMainDirName, char *strOSDirName,
342 char *strCtrlDirName)
343{
344 int err = 0;
345 char strAppend[250];
346 struct stat st;
347 char *fileLocation = NULL((void*)0);
348 char *fileName;
349 int length = 0;
350 int nIndex = 0;
351 char *strTemp = NULL((void*)0);
352 int j;
353 int k = 0;
354 int i = 0;
355
356 if (strchr(strFilePath, '/')) {
357 fileName = strrchr(strFilePath, '\\');
358 if (!fileName)
359 fileName = strrchr(strFilePath, '/');
360
361 if (fileName) {
362 if (!strcmp(fileName, "/"))
363 goto exit_status;
364
365 while (strFilePath[nIndex] != '\0') {
366 if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1])
367 goto exit_status;
368 nIndex++;
369 }
370
371 length = (int)strlen(strFilePath) - (int)strlen(fileName);
372
373 if (fileName == strFilePath)
374 length = 1;
375
376 fileLocation = (char *)malloc(length + 1);
377 if (!fileLocation)
378 goto exit_status;
379 strncpy(fileLocation, strFilePath, length);
380 fileLocation[length] = '\0';
381
382 while (fileLocation[k] != '\0') {
383 if (fileLocation[k] == '\\')
384 fileLocation[k] = '/';
385 k++;
386 }
387
388 length = (int)strlen(fileLocation);
389
390 if (':' == fileLocation[length - 1]) {
391 strTemp = (char *)malloc(length + 2);
392 if (!strTemp) {
393 free(fileLocation);
394 goto exit_status;
395 }
396 strcpy(strTemp, fileLocation);
397 strcat(strTemp, "/");
398 free(fileLocation);
399
400 length = (int)strlen(strTemp);
401 fileLocation = (char *)malloc(length + 1);
402 if (!fileLocation) {
403 free(strTemp);
404 goto exit_status;
405 }
406
407 memcpy(fileLocation, strTemp, length + 1);
408 free(strTemp);
409 }
410
411 if (stat(fileLocation, &st)) {
412 free(fileLocation);
413 goto exit_status;
414 }
415 free(fileLocation);
416 } else {
417 goto exit_status;
418 }
419 }
420
421 nIndex = 0;
422 for (i = 0; i < (int)strlen(strSN); i++) {
423 if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r')
424 strMainDirName[nIndex++] = strSN[i];
425 }
426 strMainDirName[nIndex] = '\0';
427
428 j = 1;
429 while (mkdir(strMainDirName, 0777) < 0) {
430 if (errno(*__errno_location ()) != EEXIST17) {
431 err = -1;
432 goto exit_status;
433 }
434 strMainDirName[nIndex] = '\0';
435 sprintf(strAppend, "-%d", j);
436 strcat(strMainDirName, strAppend);
437 j++;
438 }
439
440 if (strOSDirName) {
441 sprintf(strOSDirName, "%s/%s", strMainDirName, "OS");
442 if (mkdir(strOSDirName, 0777) < 0) {
443 rmdir(strMainDirName);
444 err = -1;
445 goto exit_status;
446 }
447 }
448 if (strCtrlDirName) {
449 sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller");
450 if (mkdir(strCtrlDirName, 0777) < 0) {
451 if (strOSDirName)
452 rmdir(strOSDirName);
453 rmdir(strMainDirName);
454 err = -1;
455 }
456 }
457
458exit_status:
459 return err;
460}
461
462static int GetLogPageSize(struct libnvme_transport_handle *hdl, unsigned char ucLogID, int *nLogSize)
463{
464 int err = 0;
465 unsigned char pTmpBuf[CommonChunkSize(16 * 4096)] = { 0 };
466 struct LogPageHeader_t *pLogHeader = NULL((void*)0);
467
468 if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) {
469 err = nvme_get_log_simple(hdl, ucLogID, pTmpBuf, CommonChunkSize(16 * 4096));
470 if (!err) {
471 pLogHeader = (struct LogPageHeader_t *) pTmpBuf;
472 struct LogPageHeader_t *pLogHeader1 = (struct LogPageHeader_t *) pLogHeader;
473 *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4;
474 if (!pLogHeader1->logPageHeaderFormatVersion) {
475 printf("Unsupported log page format version %d of log page : 0x%X\n",
476 ucLogID, err);
477 *nLogSize = 0;
478 err = -1;
479 }
480 } else {
481 printf("Getting size of log page : 0x%X failed with %d (ignored)!\n",
482 ucLogID, err);
483 *nLogSize = 0;
484 }
485 }
486 return err;
487}
488
489static int NVMEGetLogPage(struct libnvme_transport_handle *hdl, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize,
490 int offset)
491{
492 int err = 0;
493 struct libnvme_passthru_cmd cmd = { 0 };
494 unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int);
495 unsigned int uiMaxChunk = uiNumDwords;
496 unsigned int uiNumChunks = 1;
497 unsigned int uiXferDwords = 0;
498 unsigned long long ullBytesRead = offset;
499 unsigned char *pTempPtr = pBuffer;
500 unsigned char ucOpCode = 0x02;
501
502 if (!ullBytesRead && (ucLogID == 0xE6 || ucLogID == 0xE7))
503 uiMaxChunk = 4096;
504 else if (uiMaxChunk > 16 * 1024)
505 uiMaxChunk = 16 * 1024;
506
507 if (ucLogID == 0xE9) {
508 uiMaxChunk = 0x1D8C;
509 ullBytesRead = offset;
510 }
511
512 uiNumChunks = uiNumDwords / uiMaxChunk;
513 if (uiNumDwords % uiMaxChunk > 0)
514 uiNumChunks += 1;
515
516 for (unsigned int i = 0; i < uiNumChunks; i++) {
517 memset(&cmd, 0, sizeof(cmd));
518 uiXferDwords = uiMaxChunk;
519 if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0)
520 uiXferDwords = uiNumDwords % uiMaxChunk;
521
522 cmd.opcode = ucOpCode;
523 cmd.cdw10 |= ucLogID;
524 cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16;
525
526 if (ucLogID == 0x7 && offset == 0)
527 cmd.cdw10 |= 0x100;
528
529 if (!ullBytesRead && (ucLogID == 0xE6 || ucLogID == 0xE7))
530 cmd.cdw11 = 1;
531 if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) {
532 unsigned long long ullOffset = ullBytesRead;
533
534 cmd.cdw12 = ullOffset & 0xFFFFFFFF;
535 cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF;
536 }
537
538 cmd.addr = (__u64) (uintptr_t) pTempPtr;
539 cmd.nsid = 0xFFFFFFFF;
540 cmd.data_len = uiXferDwords * 4;
541 err = libnvme_exec_admin_passthru(hdl, &cmd);
542 ullBytesRead += uiXferDwords * 4;
543 if (ucLogID == 0x07 || ucLogID == 0x08 || ucLogID == 0xE9)
544 pTempPtr = pBuffer + (ullBytesRead - offset);
545 else
546 pTempPtr = pBuffer + ullBytesRead;
547 }
548
549 return err;
550}
551
552static int NVMEResetLog(struct libnvme_transport_handle *hdl, unsigned char ucLogID, int nBufferSize,
553 long long llMaxSize)
554{
555 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned int *pBuffer = NULL((void*)0);
556 int err = 0;
557
558 pBuffer = (unsigned int *)libnvme_alloc(nBufferSize);
559 if (!pBuffer)
560 return err;
561
562 while (!err && llMaxSize > 0) {
563 err = NVMEGetLogPage(hdl, ucLogID, (unsigned char *)pBuffer, nBufferSize, 0);
564 if (err)
565 return err;
566
567 if (pBuffer[0] == 0xdeadbeef)
568 break;
569
570 llMaxSize = llMaxSize - nBufferSize;
571 }
572
573 return err;
574}
575
576static int GetCommonLogPage(struct libnvme_transport_handle *hdl, unsigned char ucLogID,
577 unsigned char **pBuffer, int nBuffSize)
578{
579 unsigned char *pTempPtr = NULL((void*)0);
580 int err = 0;
581
582 pTempPtr = (unsigned char *)libnvme_alloc(nBuffSize);
583 if (!pTempPtr)
584 goto exit_status;
585 err = nvme_get_log_simple(hdl, ucLogID, pTempPtr, nBuffSize);
586 *pBuffer = pTempPtr;
587
588exit_status:
589 return err;
590}
591
592/*
593 * Plugin Commands
594 */
595static int micron_parse_options(struct libnvme_global_ctx **ctx,
596 struct libnvme_transport_handle **hdl, int argc,
597 char **argv, const char *desc,
598 struct argconfig_commandline_options *opts,
599 enum eDriveModel *modelp)
600{
601 int err = parse_and_open(ctx, hdl, argc, argv, desc, opts);
602
603 if (err) {
604 perror("open");
605 return -1;
606 }
607
608 if (modelp)
609 *modelp = GetDriveModel(*ctx, *hdl);
610
611 return 0;
612}
613
614static int micron_fw_commit(struct libnvme_transport_handle *hdl, int select)
615{
616 struct libnvme_passthru_cmd cmd = {
617 .opcode = nvme_admin_fw_commit,
618 .cdw10 = 8,
619 .cdw12 = select,
620 };
621
622 return libnvme_exec_admin_passthru(hdl, &cmd);
623}
624
625static int micron_selective_download(int argc, char **argv,
626 struct command *command, struct plugin *plugin)
627{
628 const char *desc =
629 "This performs a selective firmware download, which allows the user to\n"
630 "select which firmware binary to update for 9200 devices. This requires\n"
631 "a power cycle once the update completes. The options available are:\n\n"
632 "OOB - This updates the OOB and main firmware\n"
633 "EEP - This updates the eeprom and main firmware\n"
634 "ALL - This updates the eeprom, OOB, and main firmware";
635 const char *fw = "firmware file (required)";
636 const char *select = "FW Select (e.g., --select=ALL)";
637
638 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
639 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
640
641 int selectNo, fw_fd, fw_size, err, offset = 0;
642 struct libnvme_passthru_cmd cmd;
643 int xfer = 4096;
644 struct stat sb;
645 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) void *fw_buf = NULL((void*)0);
646 unsigned char *fw_ptr;
647
648 struct config {
649 char *fw;
650 char *select;
651 };
652
653 struct config cfg = {
654 .fw = "",
655 .select = "\0",
656 };
657
658 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"fw", 'f', "FILE", CFG_STRING, &cfg.fw, 1, fw, 0,
}, {"select", 's', "flag", CFG_STRING, &cfg.select, 1, select
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
659 OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"fw", 'f', "FILE", CFG_STRING, &cfg.fw, 1, fw, 0,
}, {"select", 's', "flag", CFG_STRING, &cfg.select, 1, select
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
660 OPT_STRING("select", 's', "flag", &cfg.select, select))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"fw", 'f', "FILE", CFG_STRING, &cfg.fw, 1, fw, 0,
}, {"select", 's', "flag", CFG_STRING, &cfg.select, 1, select
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
661
662 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
663 if (err)
664 return err;
665
666 if (strlen(cfg.select) != 3) {
667 fprintf(stderrstderr, "Invalid select flag\n");
668 return -EINVAL22;
669 }
670
671 for (int i = 0; i < 3; i++)
672 cfg.select[i] = toupper(cfg.select[i])(__extension__ ({ int __res; if (sizeof (cfg.select[i]) > 1
) { if (__builtin_constant_p (cfg.select[i])) { int __c = (cfg
.select[i]); __res = __c < -128 || __c > 255 ? __c : (*
__ctype_toupper_loc ())[__c]; } else __res = toupper (cfg.select
[i]); } else __res = (*__ctype_toupper_loc ())[(int) (cfg.select
[i])]; __res; }))
;
673
674 if (!strncmp(cfg.select, "OOB", 3)) {
675 selectNo = 18;
676 } else if (!strncmp(cfg.select, "EEP", 3)) {
677 selectNo = 10;
678 } else if (!strncmp(cfg.select, "ALL", 3)) {
679 selectNo = 26;
680 } else {
681 fprintf(stderrstderr, "Invalid select flag\n");
682 return -EINVAL22;
683 }
684
685 fw_fd = open(cfg.fw, O_RDONLY00);
686 if (fw_fd < 0) {
687 fprintf(stderrstderr, "no firmware file provided\n");
688 return -EINVAL22;
689 }
690
691 err = fstat(fw_fd, &sb);
692 if (err < 0) {
693 perror("fstat");
694 err = errno(*__errno_location ());
695 goto out;
696 }
697
698 fw_size = sb.st_size;
699 if (fw_size & 0x3) {
700 fprintf(stderrstderr, "Invalid size:%d for f/w image\n", fw_size);
701 err = EINVAL22;
702 goto out;
703 }
704
705 fw_buf = libnvme_alloc(fw_size);
706 if (!fw_buf) {
707 fprintf(stderrstderr, "No memory for f/w size:%d\n", fw_size);
708 err = ENOMEM12;
709 goto out;
710 }
711 fw_ptr = fw_buf;
712
713 if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) {
714 err = errno(*__errno_location ());
715 goto out;
716 }
717
718 while (fw_size > 0) {
719 xfer = min(xfer, fw_size)((xfer) > (fw_size) ? (fw_size) : (xfer));
720
721 err = nvme_init_fw_download(&cmd, fw_ptr, xfer, offset);
722 if (err) {
723 perror("fw-download");
724 goto out;
725 }
726 err = libnvme_exec_admin_passthru(hdl, &cmd);
727 if (err < 0) {
728 perror("fw-download");
729 goto out;
730 } else if (err) {
731 nvme_show_status(err);
732 goto out;
733 }
734 fw_ptr += xfer;
735 fw_size -= xfer;
736 offset += xfer;
737 }
738
739 err = micron_fw_commit(hdl, selectNo);
740
741 if (err == 0x10B || err == 0x20B) {
742 err = 0;
743 fprintf(stderrstderr,
744 "Update successful! Power cycle for changes to take effect\n");
745 }
746
747out:
748 close(fw_fd);
749 return err;
750}
751
752static int micron_smbus_option(int argc, char **argv,
753 struct command *command, struct plugin *plugin)
754{
755 __u64 result = 0;
756 __u32 cdw11 = 0;
757 const char *desc = "Enable/Disable/Get status of SMBUS option on controller";
758 const char *option = "enable or disable or status";
759 const char *value =
760 "1 - hottest component temperature, 0 - composite temperature (default) for enable option, 0 (current), 1 (default), 2 (saved) for status options";
761
762 const char *save = "1 - persistent, 0 - non-persistent (default)";
763 int fid = MICRON_FEATURE_SMBUS_OPTION0xD5;
764 enum eDriveModel model = UNKNOWN_MODEL;
765 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
766 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
767 int err = 0;
768
769 struct {
770 char *option;
771 int value;
772 int save;
773 int status;
774 } opt = {
775 .option = "disable",
776 .value = 0,
777 .save = 0,
778 .status = 0,
779 };
780
781 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"value", 'V', "NUM", CFG_POSITIVE, &opt
.value, 1, value, 0, }, {"save", 's', "NUM", CFG_POSITIVE, &
opt.save, 1, save, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
782 OPT_STRING("option", 'O', "option", &opt.option, option),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"value", 'V', "NUM", CFG_POSITIVE, &opt
.value, 1, value, 0, }, {"save", 's', "NUM", CFG_POSITIVE, &
opt.save, 1, save, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
783 OPT_UINT("value", 'V', &opt.value, value),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"value", 'V', "NUM", CFG_POSITIVE, &opt
.value, 1, value, 0, }, {"save", 's', "NUM", CFG_POSITIVE, &
opt.save, 1, save, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
784 OPT_UINT("save", 's', &opt.save, save))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"value", 'V', "NUM", CFG_POSITIVE, &opt
.value, 1, value, 0, }, {"save", 's', "NUM", CFG_POSITIVE, &
opt.save, 1, save, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
;
785
786 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
787 if (err < 0)
788 return err;
789
790 if (model != M5407 && model != M5411 && model != M6003 && model != M6004) {
791 printf("This option is not supported for specified drive\n");
792 return err;
793 }
794
795 if (!strcmp(opt.option, "enable")) {
796 cdw11 = opt.value << 1 | 1;
797 err = nvme_set_features_simple(hdl, 1, fid, opt.save, cdw11,
798 &result);
799 if (!err)
800 printf("successfully enabled SMBus on drive\n");
801 else
802 printf("Failed to enabled SMBus on drive\n");
803 } else if (!strcmp(opt.option, "status")) {
804 err = nvme_get_features(hdl, 1, fid, opt.value, 0, 0, NULL((void*)0), 0, &result);
805 if (!err)
806 printf("SMBus status on the drive: %s (returns %s temperature)\n",
807 (result & 1) ? "enabled" : "disabled",
808 (result & 2) ? "hottest component" : "composite");
809 else
810 printf("Failed to retrieve SMBus status on the drive\n");
811 } else if (!strcmp(opt.option, "disable")) {
812 cdw11 = opt.value << 1 | 0;
813 err = nvme_set_features_simple(hdl, 1, fid, opt.save, cdw11,
814 &result);
815 if (!err)
816 printf("Successfully disabled SMBus on drive\n");
817 else
818 printf("Failed to disable SMBus on drive\n");
819 } else {
820 printf("Invalid option %s, valid values are enable, disable or status\n",
821 opt.option);
822 return -1;
823 }
824
825 return err;
826}
827
828static int micron_temp_stats(int argc, char **argv, struct command *acmd,
829 struct plugin *plugin)
830{
831
832 struct nvme_smart_log smart_log;
833 unsigned int temperature = 0, i = 0, err = 0;
834 unsigned int tempSensors[SensorCount8] = { 0 };
835 const char *desc = "Retrieve Micron temperature info for the given device ";
836 const char *fmt = "output format normal|json";
837 nvme_print_flags_t flags;
838 struct format {
839 char *fmt;
840 };
841 struct format cfg = {
842 .fmt = "normal",
843 };
844 bool_Bool is_json = false0;
845 struct json_object *root;
846 struct json_object *logPages;
847 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
848 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
849 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
850 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
851
852 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
853 if (err) {
854 printf("\nDevice not found\n");
855 return -1;
856 }
857
858 err = validate_output_format(nvme_args.output_format, &flags);
859 if (err < 0) {
860 nvme_show_error("Invalid output format")nvme_show_message(1, "Invalid output format");
861 return err;
862 }
863
864 if (!strcmp(cfg.fmt, "json") || flags & JSON)
865 is_json = true1;
866
867 err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log);
868 if (!err) {
869 temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]);
870 temperature = temperature ? temperature - 273 : 0;
871 for (i = 0; i < SensorCount8 && tempSensors[i]; i++) {
872 tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]);
873 tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0;
874 }
875 if (is_json) {
876 struct json_object *stats = json_create_object()json_object_new_object();
877 char tempstr[64] = { 0 };
878
879 root = json_create_object()json_object_new_object();
880 logPages = json_create_array()json_object_new_array();
881 json_object_add_value_array(root, "Micron temperature information", logPages)json_object_object_add(root, "Micron temperature information"
, logPages)
;
882 sprintf(tempstr, "%u C", temperature);
883 json_object_add_value_string(stats, "Current Composite Temperature", tempstr);
884 for (i = 0; i < SensorCount8 && tempSensors[i]; i++) {
885 char sensor_str[256] = { 0 };
886 char datastr[64] = { 0 };
887
888 sprintf(sensor_str, "Temperature Sensor #%d", (i + 1));
889 sprintf(datastr, "%u C", tempSensors[i]);
890 json_object_add_value_string(stats, sensor_str, datastr);
891 }
892 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
893 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
894 printf("\n");
895 json_free_object(root)json_object_put(root);
896 } else {
897 printf("Micron temperature information:\n");
898 printf("%-10s : %u C\n", "Current Composite Temperature", temperature);
899 for (i = 0; i < SensorCount8 && tempSensors[i]; i++)
900 printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]);
901 }
902 }
903 return err;
904}
905
906struct pcie_error_counters {
907 __u16 receiver_error;
908 __u16 bad_tlp;
909 __u16 bad_dllp;
910 __u16 replay_num_rollover;
911 __u16 replay_timer_timeout;
912 __u16 advisory_non_fatal_error;
913 __u16 DLPES;
914 __u16 poisoned_tlp;
915 __u16 FCPC;
916 __u16 completion_timeout;
917 __u16 completion_abort;
918 __u16 unexpected_completion;
919 __u16 receiver_overflow;
920 __u16 malformed_tlp;
921 __u16 ecrc_error;
922 __u16 unsupported_request_error;
923} pcie_error_counters = { 0 };
924
925struct {
926 const char *err;
927 int bit;
928 int val;
929} pcie_correctable_errors[] = {
930 { (char *)"Unsupported Request Error Status (URES)", 20,
931 offsetof(struct pcie_error_counters, unsupported_request_error)__builtin_offsetof(struct pcie_error_counters, unsupported_request_error
)
},
932 { (char *)"ECRC Error Status (ECRCES)", 19,
933 offsetof(struct pcie_error_counters, ecrc_error)__builtin_offsetof(struct pcie_error_counters, ecrc_error)},
934 { (char *)"Malformed TLP Status (MTS)", 18,
935 offsetof(struct pcie_error_counters, malformed_tlp)__builtin_offsetof(struct pcie_error_counters, malformed_tlp)},
936 { (char *)"Receiver Overflow Status (ROS)", 17,
937 offsetof(struct pcie_error_counters, receiver_overflow)__builtin_offsetof(struct pcie_error_counters, receiver_overflow
)
},
938 { (char *)"Unexpected Completion Status (UCS)", 16,
939 offsetof(struct pcie_error_counters, unexpected_completion)__builtin_offsetof(struct pcie_error_counters, unexpected_completion
)
},
940 { (char *)"Completer Abort Status (CAS)", 15,
941 offsetof(struct pcie_error_counters, completion_abort)__builtin_offsetof(struct pcie_error_counters, completion_abort
)
},
942 { (char *)"Completion Timeout Status (CTS)", 14,
943 offsetof(struct pcie_error_counters, completion_timeout)__builtin_offsetof(struct pcie_error_counters, completion_timeout
)
},
944 { (char *)"Flow Control Protocol Error Status (FCPES)", 13,
945 offsetof(struct pcie_error_counters, FCPC)__builtin_offsetof(struct pcie_error_counters, FCPC)},
946 { (char *)"Poisoned TLP Status (PTS)", 12,
947 offsetof(struct pcie_error_counters, poisoned_tlp)__builtin_offsetof(struct pcie_error_counters, poisoned_tlp)},
948 { (char *)"Data Link Protocol Error Status (DLPES)", 4,
949 offsetof(struct pcie_error_counters, DLPES)__builtin_offsetof(struct pcie_error_counters, DLPES)},
950 },
951 pcie_uncorrectable_errors[] = {
952 { (char *)"Advisory Non-Fatal Error Status (ANFES)", 13,
953 offsetof(struct pcie_error_counters, advisory_non_fatal_error)__builtin_offsetof(struct pcie_error_counters, advisory_non_fatal_error
)
},
954 { (char *)"Replay Timer Timeout Status (RTS)", 12,
955 offsetof(struct pcie_error_counters, replay_timer_timeout)__builtin_offsetof(struct pcie_error_counters, replay_timer_timeout
)
},
956 { (char *)"REPLAY_NUM Rollover Status (RRS)", 8,
957 offsetof(struct pcie_error_counters, replay_num_rollover)__builtin_offsetof(struct pcie_error_counters, replay_num_rollover
)
},
958 { (char *)"Bad DLLP Status (BDS)", 7,
959 offsetof(struct pcie_error_counters, bad_dllp)__builtin_offsetof(struct pcie_error_counters, bad_dllp)},
960 { (char *)"Bad TLP Status (BTS)", 6,
961 offsetof(struct pcie_error_counters, bad_tlp)__builtin_offsetof(struct pcie_error_counters, bad_tlp)},
962 { (char *)"Receiver Error Status (RES)", 0,
963 offsetof(struct pcie_error_counters, receiver_error)__builtin_offsetof(struct pcie_error_counters, receiver_error
)
},
964 };
965
966
967static int micron_pcie_stats(int argc, char **argv,
968 struct command *command, struct plugin *plugin)
969{
970 int i, err = 0;
971 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
972 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
973 nvme_print_flags_t flags;
974 struct libnvme_passthru_cmd admin_cmd = { 0 };
975 enum eDriveModel eModel = UNKNOWN_MODEL;
976 bool_Bool is_json = true1;
977 bool_Bool counters = false0;
978 struct format {
979 char *fmt;
980 };
981 const char *desc = "Retrieve PCIe event counters";
982 const char *fmt = "output format json|normal";
983 struct format cfg = {
984 .fmt = "json",
985 };
986
987 __u32 correctable_errors = 0;
988 __u32 uncorrectable_errors = 0;
989
990 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
991 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
992
993 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
994 if (err) {
995 printf("\nDevice not found\n");
996 return -1;
997 }
998
999 err = validate_output_format(nvme_args.output_format, &flags);
1000 if (err < 0) {
1001 nvme_show_error("Invalid output format")nvme_show_message(1, "Invalid output format");
1002 return err;
1003 }
1004
1005 /* pull log details based on the model name */
1006 eModel = GetDriveModel(ctx, hdl);
1007 if (eModel == UNKNOWN_MODEL) {
1008 printf("Unsupported drive model for vs-pcie-stats command\n");
1009 goto out;
1010 }
1011
1012 if (!strcmp(cfg.fmt, "normal") || flags & NORMAL)
1013 is_json = false0;
1014
1015 if (eModel == M5407) {
1016 admin_cmd.opcode = 0xD6;
1017 admin_cmd.addr = (__u64)(uintptr_t)&pcie_error_counters;
1018 admin_cmd.data_len = sizeof(pcie_error_counters);
1019 admin_cmd.cdw10 = 1;
1020 err = libnvme_exec_admin_passthru(hdl, &admin_cmd);
1021 if (!err) {
1022 counters = true1;
1023 correctable_errors = 10;
1024 uncorrectable_errors = 6;
1025 goto print_stats;
1026 }
1027 }
1028
1029 err = micron_get_pcie_aer_errors(hdl, &correctable_errors,
1030 &uncorrectable_errors);
1031 if (err)
1032 goto out;
1033print_stats:
1034 if (is_json) {
1035 struct json_object *root = json_create_object()json_object_new_object();
1036 struct json_object *pcieErrors = json_create_array()json_object_new_array();
1037 struct json_object *stats = json_create_object()json_object_new_object();
1038 __u8 *pcounter = (__u8 *)&pcie_error_counters;
1039
1040 json_object_add_value_array(root, "PCIE Stats", pcieErrors)json_object_object_add(root, "PCIE Stats", pcieErrors);
1041 for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors)(sizeof(pcie_correctable_errors) / sizeof((pcie_correctable_errors
)[0]))
; i++) {
1042 __u16 val = counters ? *(__u16 *)(pcounter + pcie_correctable_errors[i].val) :
1043 (correctable_errors >> pcie_correctable_errors[i].bit) & 1;
1044 json_object_add_value_int(stats, pcie_correctable_errors[i].err, val)json_object_object_add(stats, pcie_correctable_errors[i].err,
json_object_new_int(val))
;
1045 }
1046 for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors)(sizeof(pcie_uncorrectable_errors) / sizeof((pcie_uncorrectable_errors
)[0]))
; i++) {
1047 __u16 val = counters ? *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val) :
1048 (uncorrectable_errors >>
1049 pcie_uncorrectable_errors[i].bit) & 1;
1050 json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, val)json_object_object_add(stats, pcie_uncorrectable_errors[i].err
, json_object_new_int(val))
;
1051 }
1052 json_array_add_value_object(pcieErrors, stats)json_object_array_add(pcieErrors, stats);
1053 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1054 printf("\n");
1055 json_free_object(root)json_object_put(root);
1056 } else if (counters == true1) {
1057 __u8 *pcounter = (__u8 *)&pcie_error_counters;
1058
1059 for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors)(sizeof(pcie_correctable_errors) / sizeof((pcie_correctable_errors
)[0]))
; i++)
1060 printf("%-42s : %-1hu\n", pcie_correctable_errors[i].err,
1061 *(__u16 *)(pcounter + pcie_correctable_errors[i].val));
1062 for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors)(sizeof(pcie_uncorrectable_errors) / sizeof((pcie_uncorrectable_errors
)[0]))
; i++)
1063 printf("%-42s : %-1hu\n", pcie_uncorrectable_errors[i].err,
1064 *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val));
1065 } else if (eModel == M5407 || eModel == M5410) {
1066 for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors)(sizeof(pcie_correctable_errors) / sizeof((pcie_correctable_errors
)[0]))
; i++)
1067 printf("%-42s : %-1d\n", pcie_correctable_errors[i].err,
1068 ((correctable_errors >>
1069 pcie_correctable_errors[i].bit) & 1));
1070 for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors)(sizeof(pcie_uncorrectable_errors) / sizeof((pcie_uncorrectable_errors
)[0]))
; i++)
1071 printf("%-42s : %-1d\n", pcie_uncorrectable_errors[i].err,
1072 ((uncorrectable_errors >>
1073 pcie_uncorrectable_errors[i].bit) & 1));
1074 } else {
1075 printf("PCIE Stats:\n");
1076 printf("Device correctable errors detected: 0x%x\n",
1077 correctable_errors);
1078 printf("Device uncorrectable errors detected: 0x%x\n",
1079 uncorrectable_errors);
1080 }
1081
1082out:
1083 return err;
1084}
1085
1086static int micron_clear_pcie_correctable_errors(int argc, char **argv,
1087 struct command *command,
1088 struct plugin *plugin)
1089{
1090 int err = -EINVAL22;
1091 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1092 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1093 enum eDriveModel model = UNKNOWN_MODEL;
1094 struct libnvme_passthru_cmd admin_cmd = { 0 };
1095 const char *desc = "Clear PCIe Device Correctable Errors";
1096 __u64 result = 0;
1097 __u8 fid = MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS0xC3;
1098
1099 NVME_ARGS(opts)struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
1100
1101 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
1102 if (err < 0)
1103 return err;
1104
1105 /* For M51CX models, PCIe errors are cleared using 0xC3 feature
1106 * and for M5407 models, PCIe errors are cleared using 0xD6 command
1107 * If these fail, proceed with sysfs interface to set/clear bits
1108 */
1109 if (model == M51CX || model == M51BY || model == M51CY) {
1110 err = nvme_set_features_simple(hdl, 0, fid, false0, (1 << 31),
1111 &result);
1112 if (!err)
1113 err = (int)result;
1114 if (!err) {
1115 printf("Device correctable errors are cleared!\n");
1116 return 0;
1117 }
1118 } else if (model == M5407) {
1119 admin_cmd.opcode = 0xD6;
1120 admin_cmd.addr = 0;
1121 admin_cmd.cdw10 = 0;
1122 err = libnvme_exec_admin_passthru(hdl, &admin_cmd);
1123 if (!err) {
1124 printf("Device correctable error counters are cleared!\n");
1125 return 0;
1126 }
1127 }
1128
1129 /* clear status bits using system commands */
1130 err = micron_clear_pcie_aer_correctable_errors(hdl);
1131
1132 return err;
1133}
1134
1135static struct logpage {
1136 const char *field;
1137 char datastr[128];
1138} d0_log_page[] = {
1139 { "NAND Writes (Bytes Written)", { 0 }},
1140 { "Program Failure Count", { 0 }},
1141 { "Erase Failures", { 0 }},
1142 { "Bad Block Count", { 0 }},
1143 { "NAND XOR/RAID Recovery Trigger Events", { 0 }},
1144 { "NSZE Change Supported", { 0 }},
1145 { "Number of NSZE Modifications", { 0 }}
1146};
1147
1148static void init_d0_log_page(__u8 *buf, __u8 nsze)
1149{
1150 unsigned int logD0[D0_log_size512/sizeof(int)] = { 0 };
1151 __u64 count_lo, count_hi, count;
1152
1153 memcpy(logD0, buf, sizeof(logD0));
1154
1155
1156 count = ((__u64)logD0[45] << 32) | logD0[44];
1157 sprintf(d0_log_page[0].datastr, "0x%"PRIx64"l" "x", le64_to_cpu(count));
1158
1159 count_hi = ((__u64)logD0[39] << 32) | logD0[38];
1160 count_lo = ((__u64)logD0[37] << 32) | logD0[36];
1161 if (count_hi)
1162 sprintf(d0_log_page[1].datastr, "0x%"PRIx64"l" "x""%016"PRIx64"l" "x",
1163 le64_to_cpu(count_hi), le64_to_cpu(count_lo));
1164 else
1165 sprintf(d0_log_page[1].datastr, "0x%"PRIx64"l" "x", le64_to_cpu(count_lo));
1166
1167 count = ((__u64)logD0[25] << 32) | logD0[24];
1168 sprintf(d0_log_page[2].datastr, "0x%"PRIx64"l" "x", le64_to_cpu(count));
1169
1170 sprintf(d0_log_page[3].datastr, "0x%x", logD0[3]);
1171
1172 count_lo = ((__u64)logD0[37] << 32) | logD0[36];
1173 count = ((__u64)logD0[25] << 32) | logD0[24];
1174 count = (__u64)logD0[3] - (count_lo + count);
1175 sprintf(d0_log_page[4].datastr, "0x%"PRIx64"l" "x", le64_to_cpu(count));
1176
1177 sprintf(d0_log_page[5].datastr, "0x%x", nsze);
1178 sprintf(d0_log_page[6].datastr, "0x%x", logD0[1]);
1179}
1180
1181static struct nand_stats {
1182 const char *field;
1183 char datastr[128];
1184} hyperscale_BSSD_nand_stats[] = {
1185 {"Physical Media Units Written - TLC", {0}},
1186 {"Physical Media Units Written - SLC", {0}},
1187 {"Bad User NAND Block Count (Normalized)", {0}},
1188 {"Bad User NAND Block Count (Raw)", {0}},
1189 {"XOR Recovery count", {0}},
1190 {"Uncorrectable read error count", {0}},
1191 { "User Data Erase Counts (Minimum TLC)", {0}},
1192 { "User Data Erase Counts (Maximum TLC)", {0}},
1193 { "User Data Erase Counts (Average TLC)", {0}},
1194 { "User Data Erase Counts (Minimum SLC)", {0}},
1195 { "User Data Erase Counts (Maximum SLC)", {0}},
1196 { "User Data Erase Counts (Average SLC)", {0}},
1197 { "Program Fail Count (Normalized)", {0}},
1198 { "Program Fail Count (Raw)", {0}},
1199 { "Erase Fail Count (Normalized)", {0}},
1200 { "Erase Fail Count (Raw)", {0}},
1201 { "Total # of Soft ECC Error Count", {0}},
1202 { "Bad System NAND Block Count (Normalized)", {0}},
1203 { "Bad System NAND Block Count (Raw)", {0}},
1204 {"Endurance Estimate", {0}},
1205 {"Physical Media Units Read", {0}},
1206 {"Boot SSD Spec Version", {0}},
1207};
1208
1209static void micron_readFixedBytesFromBuffer(__u8 *buf, int offset, int numBytes, char *datastr)
1210{
1211 __u16 u16Val;
1212 __u32 u32Val;
1213 __u64 count_lo, count_hi, count;
1214
1215 switch (numBytes) {
1216 case 16:
1217 {
1218 count_lo = *((__u64 *)(&buf[offset]));
1219 count_hi = *((__u64 *)(&buf[offset + 8]));
1220 if (count_hi)
1221 sprintf(datastr, "0x%"PRIx64"l" "x""%016"PRIx64"l" "x""",
1222 le64_to_cpu(count_hi),
1223 le64_to_cpu(count_lo));
1224 else
1225 sprintf(datastr, "0x%"PRIx64"l" "x""", le64_to_cpu(count_lo));
1226
1227 }
1228 break;
1229 case 8:
1230 {
1231 count = *((__u64 *)(&buf[offset]));
1232 sprintf(datastr, "0x%"PRIx64"l" "x""", le64_to_cpu(count));
1233 }
1234 break;
1235 case 6:
1236 {
1237 u32Val = *((__u32 *)(&buf[offset]));
1238 u16Val = *((__u16 *)(&buf[offset + 4]));
1239 count = (((__u64)u32Val << 32) | u16Val);
1240 sprintf(datastr, "0x%"PRIx64"l" "x""", le64_to_cpu(count));
1241 }
1242 break;
1243 case 2:
1244 {
1245 u16Val = *((__u16 *)(&buf[offset]));
1246 sprintf(datastr, "0x%04x", le16_to_cpu(u16Val));
1247 }
1248 break;
1249 }
1250}
1251
1252static void init_hyperscale_BSSD_nand_stats(__u8 *buf)
1253{
1254
1255 __u16 majorVer, minorVer, pointVer, errataVer;
1256
1257 micron_readFixedBytesFromBuffer(buf, 0, 16, hyperscale_BSSD_nand_stats[0].datastr);
1258 micron_readFixedBytesFromBuffer(buf, 16, 16, hyperscale_BSSD_nand_stats[1].datastr);
1259 micron_readFixedBytesFromBuffer(buf, 32, 2, hyperscale_BSSD_nand_stats[2].datastr);
1260 micron_readFixedBytesFromBuffer(buf, 34, 6, hyperscale_BSSD_nand_stats[3].datastr);
1261 micron_readFixedBytesFromBuffer(buf, 40, 8, hyperscale_BSSD_nand_stats[4].datastr);
1262 micron_readFixedBytesFromBuffer(buf, 48, 8, hyperscale_BSSD_nand_stats[5].datastr);
1263 micron_readFixedBytesFromBuffer(buf, 84, 8, hyperscale_BSSD_nand_stats[6].datastr);
1264 micron_readFixedBytesFromBuffer(buf, 92, 8, hyperscale_BSSD_nand_stats[7].datastr);
1265 micron_readFixedBytesFromBuffer(buf, 100, 8, hyperscale_BSSD_nand_stats[8].datastr);
1266 micron_readFixedBytesFromBuffer(buf, 108, 8, hyperscale_BSSD_nand_stats[9].datastr);
1267 micron_readFixedBytesFromBuffer(buf, 116, 8, hyperscale_BSSD_nand_stats[10].datastr);
1268 micron_readFixedBytesFromBuffer(buf, 124, 8, hyperscale_BSSD_nand_stats[11].datastr);
1269 micron_readFixedBytesFromBuffer(buf, 132, 2, hyperscale_BSSD_nand_stats[12].datastr);
1270 micron_readFixedBytesFromBuffer(buf, 134, 6, hyperscale_BSSD_nand_stats[13].datastr);
1271 micron_readFixedBytesFromBuffer(buf, 140, 2, hyperscale_BSSD_nand_stats[14].datastr);
1272 micron_readFixedBytesFromBuffer(buf, 142, 6, hyperscale_BSSD_nand_stats[15].datastr);
1273 micron_readFixedBytesFromBuffer(buf, 202, 8, hyperscale_BSSD_nand_stats[16].datastr);
1274 micron_readFixedBytesFromBuffer(buf, 218, 2, hyperscale_BSSD_nand_stats[17].datastr);
1275 micron_readFixedBytesFromBuffer(buf, 220, 6, hyperscale_BSSD_nand_stats[18].datastr);
1276 micron_readFixedBytesFromBuffer(buf, 226, 16, hyperscale_BSSD_nand_stats[19].datastr);
1277 micron_readFixedBytesFromBuffer(buf, 252, 16, hyperscale_BSSD_nand_stats[20].datastr);
1278
1279 majorVer = *((__u16 *)(&buf[300]));
1280 minorVer = *((__u16 *)(&buf[302]));
1281 pointVer = *((__u16 *)(&buf[304]));
1282 errataVer = *((__u16 *)(&buf[306]));
1283 sprintf(hyperscale_BSSD_nand_stats[21].datastr,
1284 "%x.%x.%x.%x",
1285 le16_to_cpu(majorVer),
1286 le16_to_cpu(minorVer),
1287 le16_to_cpu(pointVer),
1288 le16_to_cpu(errataVer));
1289}
1290
1291
1292/* Smart Health Log information as per OCP spec M51CX models */
1293struct request_data ocp_c0_log_page[] = {
1294 { "Physical Media Units Written", 16},
1295 { "Physical Media Units Read", 16 },
1296 { "Raw Bad User NAND Block Count", 6},
1297 { "Normalized Bad User NAND Block Count", 2},
1298 { "Raw Bad System NAND Block Count", 6},
1299 { "Normalized Bad System NAND Block Count", 2},
1300 { "XOR Recovery Count", 8},
1301 { "Uncorrectable Read Error Count", 8},
1302 { "Soft ECC Error Count", 8},
1303 { "SSD End to End Detected Counts", 4},
1304 { "SSD End to End Corrected Errors", 4},
1305 { "System data % life-used", 1},
1306 { "Refresh Count", 7},
1307 { "Maximum User Data Erase Count", 4},
1308 { "Minimum User Data Erase Count", 4},
1309 { "Thermal Throttling Count", 1},
1310 { "Thermal Throttling Status", 1},
1311 { "Reserved", 6},
1312 { "PCIe Correctable Error count", 8},
1313 { "Incomplete Shutdowns", 4},
1314 { "Reserved", 4},
1315 { "% Free Blocks", 1},
1316 { "Reserved", 7},
1317 { "Capacitor Health", 2},
1318 { "Reserved", 6},
1319 { "Unaligned I/O", 8},
1320 { "Security Version Number", 8},
1321 { "NUSE", 8},
1322 { "PLP Start Count", 16},
1323 { "Endurance Estimate", 16},
1324 { "Reserved", 302},
1325 { "Log Page Version", 2},
1326 { "Log Page GUID", 16},
1327},
1328
1329/* Smart Health Log information Extended as per Hyperscale NVME Boot SSD spec M51CX models */
1330hyperscale_c0_log_page[] = {
1331 { "Physical Media Units Written - TLC", 16},
1332 { "Physical Media Units Written - SLC", 16},
1333 { "Bad User NAND Block Count (Normalized)", 2},
1334 { "Bad User NAND Block Count (Raw)", 6},
1335 { "XOR Recovery Count", 8},
1336 { "Uncorrectable Read Error Count", 8},
1337 { "SSD End to End correction counts (Corrected Errors)", 8},
1338 { "SSD End to End correction counts (Detected Counts)", 8},
1339 { "SSD End to End correction counts (Uncorrected Counts)", 8},
1340 { "System data % life-used", 1},
1341 { "Reserved", 3},
1342 { "User Data Erase Counts (Minimum TLC)", 8},
1343 { "User Data Erase Counts (Maximum TLC)", 8},
1344 { "User Data Erase Counts (Average TLC)", 8},
1345 { "User Data Erase Counts (Minimum SLC)", 8},
1346 { "User Data Erase Counts (Maximum SLC)", 8},
1347 { "User Data Erase Counts (Average SLC)", 8},
1348 { "Program Fail Count (Normalized)", 2},
1349 { "Program Fail Count (Raw)", 6},
1350 { "Erase Fail Count (Normalized)", 2},
1351 { "Erase Fail Count (Raw)", 6},
1352 { "Pcie Correctable Error Count", 8},
1353 { "% Free Blocks (User)", 1},
1354 { "Reserved", 3},
1355 { "Security Version Number", 8},
1356 { "% Free Blocks (System)", 1},
1357 { "Reserved", 3},
1358 { "NVMe Stats (# Data set Management/TRIM Commands Completed", 16},
1359 { "Total Namespace Utilization (nvme0n1 NUSE)", 8},
1360 { "NVMe Stats (#NVMe Format Commands Completed)", 2},
1361 { "Background Back-Pressure Gauge(%)", 1},
1362 { "Reserved", 3},
1363 { "Total # of Soft ECC Error Count", 8},
1364 { "Total # of Read Refresh Count", 8},
1365 { "Bad System NAND Block Count (Normalized)", 2},
1366 { "Bad System NAND Block Count (Raw)", 6},
1367 { "Endurance Estimate (Total Writable Lifetime Bytes)", 16},
1368 { "Thermal Throttling Status & Count (Number of thermal throttling events)", 2},
1369 { "Total # Unaligned I/O", 8},
1370 { "Total Physical Media Units Read (Bytes)", 16},
1371 { "Command Timeout (# of READ CMDs exceeding threshold)", 4},
1372 { "Command Timeout (# of WRITE CMDs exceeding threshold)", 4},
1373 { "Command Timeout (# of TRIMs CMDs exceeding threshold)", 4},
1374 { "Reserved", 4},
1375 { "Total PCIe Link Retraining Count", 8},
1376 { "Active Power State Change Count", 8},
1377 { "Boot SSD Spec Version", 8},
1378 { "FTL Unit Size", 4},
1379 { "TCG Ownership Status", 4},
1380 { "Reserved", 178},
1381 { "Log Page Version", 2},
1382 { "Log Page GUID", 16},
1383},
1384
1385/* Smart Health Log information as per datacenter-nvme-ssd-specification-v2 M51BY models */
1386datacenter_c0_log_page[] = {
1387 {"Physical Media Units Written", 16},
1388 {"Physical Media Units Read", 16 },
1389 {"Raw Bad User NAND Block Count", 6},
1390 {"Normalized Bad User NAND Block Count", 2},
1391 {"Raw Bad System NAND Block Count", 6},
1392 {"Normalized Bad System NAND Block Count", 2},
1393 {"XOR Recovery Count", 8},
1394 {"Uncorrectable Read Error Count", 8},
1395 {"Soft ECC Error Count", 8},
1396 {"SSD End to End Detected Counts", 4},
1397 {"SSD End to End Corrected Errors", 4},
1398 {"System data % life-used", 1},
1399 {"Refresh Count", 7},
1400 {"Maximum User Data Erase Count", 4},
1401 {"Minimum User Data Erase Count", 4},
1402 {"Thermal Throttling Count", 1},
1403 {"Thermal Throttling Status", 1},
1404 {"DSSD Spec Version", 6},
1405 {"PCIe Correctable Error count", 8},
1406 {"Incomplete Shutdowns", 4},
1407 {"Reserved", 4},
1408 {"% Free Blocks", 1},
1409 {"Reserved", 7},
1410 {"Capacitor Health", 2},
1411 {"NVMe Errata Version", 1},
1412 {"Reserved", 5},
1413 {"Unaligned I/O", 8},
1414 {"Security Version Number", 8},
1415 {"Total NUSE", 8},
1416 {"PLP Start Count", 16},
1417 {"Endurance Estimate", 16},
1418 {"PCIe Link Retraining Count", 8},
1419 {"Power State Change Count", 8},
1420 {"Reserved", 286},
1421 {"Log Page Version", 2},
1422 {"Log Page GUID", 16},
1423},
1424
1425/* Extended SMART log information */
1426e1_log_page[] = {
1427 {"Reserved", 12},
1428 {"Grown Bad Block Count", 4},
1429 {"Per Block Max Erase Count", 4},
1430 {"Power On Minutes", 4},
1431 {"Reserved", 24},
1432 {"Write Protect Reason", 4},
1433 {"Reserved", 12},
1434 {"Drive Capacity", 8},
1435 {"Reserved", 8},
1436 {"Total Erase Count", 8},
1437 {"Lifetime Use Rate", 8},
1438 {"Erase Fail Count", 8},
1439 {"Reserved", 8},
1440 {"Reported UC Errors", 8},
1441 {"Reserved", 24},
1442 {"Program Fail Count", 16},
1443 {"Total Bytes Read", 16},
1444 {"Total Bytes Written", 16},
1445 {"Reserved", 16},
1446 {"TU Size", 4},
1447 {"Total Block Stripe Count", 4},
1448 {"Free Block Stripe Count", 4},
1449 {"Block Stripe Size", 8},
1450 {"Reserved", 16},
1451 {"User Block Min Erase Count", 4},
1452 {"User Block Avg Erase Count", 4},
1453 {"User Block Max Erase Count", 4},
1454},
1455/* Vendor Specific Health Log information */
1456fb_log_page[] = {
1457 {"Physical Media Units Written - TLC", 16, 16 },
1458 {"Physical Media Units Written - SLC", 16, 16 },
1459 {"Normalized Bad User NAND Block Count", 2, 2},
1460 {"Raw Bad User NAND Block Count", 6, 6},
1461 {"XOR Recovery Count", 8, 8},
1462 {"Uncorrectable Read Error Count", 8, 8},
1463 {"SSD End to End Corrected Errors", 8, 8},
1464 {"SSD End to End Detected Counts", 4, 8},
1465 {"SSD End to End Uncorrected Counts", 4, 8},
1466 {"System data % life-used", 1, 1},
1467 {"Reserved", 0, 3},
1468 {"Minimum User Data Erase Count - TLC", 8, 8},
1469 {"Maximum User Data Erase Count - TLC", 8, 8},
1470 {"Average User Data Erase Count - TLC", 0, 8},
1471 {"Minimum User Data Erase Count - SLC", 8, 8},
1472 {"Maximum User Data Erase Count - SLC", 8, 8},
1473 {"Average User Data Erase Count - SLC", 0, 8},
1474 {"Normalized Program Fail Count", 2, 2},
1475 {"Raw Program Fail Count", 6, 6},
1476 {"Normalized Erase Fail Count", 2, 2},
1477 {"Raw Erase Fail Count", 6, 6},
1478 {"Pcie Correctable Error Count", 8, 8},
1479 {"% Free Blocks (User)", 1, 1},
1480 {"Reserved", 0, 3},
1481 {"Security Version Number", 8, 8},
1482 {"% Free Blocks (System)", 1, 1},
1483 {"Reserved", 0, 3},
1484 {"Dataset Management (Deallocate) Commands", 16, 16},
1485 {"Incomplete TRIM Data", 8, 8},
1486 {"% Age of Completed TRIM", 1, 2},
1487 {"Background Back-Pressure Gauge", 1, 1},
1488 {"Reserved", 0, 3},
1489 {"Soft ECC Error Count", 8, 8},
1490 {"Refresh Count", 8, 8},
1491 {"Normalized Bad System NAND Block Count", 2, 2},
1492 {"Raw Bad System NAND Block Count", 6, 6},
1493 {"Endurance Estimate", 16, 16},
1494 {"Thermal Throttling Status", 1, 1},
1495 {"Thermal Throttling Count", 1, 1},
1496 {"Unaligned I/O", 8, 8},
1497 {"Physical Media Units Read", 16, 16},
1498 {"Reserved", 279, 0},
1499 {"Log Page Version", 2, 0},
1500 {"READ CMDs exceeding threshold", 0, 4},
1501 {"WRITE CMDs exceeding threshold", 0, 4},
1502 {"TRIMs CMDs exceeding threshold", 0, 4},
1503 {"Reserved", 0, 4},
1504 {"Reserved", 0, 210},
1505 {"Log Page Version", 0, 2},
1506 {"Log Page GUID", 0, 16},
1507},
1508
1509/* SMARTS for 0x6001 Nitro model */
1510//Extended Health Information (Log Identifier D0h)
1511D0_log_page[] = {
1512 {"Reserved", 2},//1:0
1513 {"Version", 2},//3:2
1514 {"Grown Bad Block Count", 4},//7:4
1515 {"Total SRAM SBE Count", 4},//11:8
1516 {"Total SRAM DBE Count", 4},//15:12
1517 {"Write Protect Reason", 4},//19:16
1518 {"Total Erase Count", 4},//23:20
1519 {"Erase Fail Count", 4},//27:24
1520 {"Program Fail Counts", 4},//31:28
1521 {"Reserved", 40},//71:32
1522 {"Completed PLN PLA Cycles", 4},//75:72
1523 {"PLN Assert Count", 4},//79:76
1524 {"PLN Deassert Count", 4},//83:80
1525 {"PLA Assert Count", 4},//87:84
1526 {"PLA Deassert Count", 4},//91:88
1527 {"Correctable NAND UECCs", 4},//95:92
1528 {"Reported Uncorrectable Errors UECCCs", 4},//99:96
1529 {"TLC Super Block Min Erase Count", 4},//103:100
1530 {"TLC Super Block Avg Erase Count", 4},//107:104
1531 {"TLC Super Block Max Erase Count", 4},//111:108
1532 {"Min ASIC Temp Recorded", 2},//113:112
1533 {"Max ASIC Temp Recorded", 2},//115:114
1534 {"Min NAND Temp Recorded", 2},//171:116
1535 {"Max NAND Temp Recorded", 2},//119:118
1536 {"SLC Super Block Min Erase Count", 4},//123:120
1537 {"SLC Super Block Avg Erase Count", 4},//127:124
1538 {"SLC Super Block Max Erase Count", 4},//131:128
1539 {"SLC Lifetime Used", 2},//133:132
1540 {"TLC Lifetime Used", 2},//135:134
1541 {"Unmapped LBA Count", 8},//143:136
1542 {"PWR1 Voltage Detection Threshold #1", 4},//147:144
1543 {"PWR1 Voltage Detection Threshold #2", 4},//151:148
1544},
1545//Micron Workload Log (Log Identifier C5h)
1546C5_log_page[] = {
1547 {"Reserved", 4},//3:0
1548 {"Number of Downshifts", 1},//4
1549 {"Reserved", 71},//75:5
1550 {"Number of LBAs Deallocated Trimmed", 4},//79:76
1551 {"Reserved", 41},//120:80
1552 {"Number of Security Send Commands", 4},//124:121
1553 {"Number of Security Receive Commands", 4},//128:125
1554 {"Total Sanitize Events", 4},//132:129
1555},
1556//Vendor Telemetry Log (Log Identifier C6h))
1557C6_log_page[] = {
1558 {"Reserved", 240},//239:0
1559 {"Total TLC NAND Write Count", 8},//247:240
1560 {"Total SLC NAND Write Count", 8},//255:248
1561 {"Total SLC Host Write Count", 8},//263:256
1562 {"Total TLC Host Write Count", 8},//272:264
1563};
1564
1565static void print_smart_cloud_health_log(__u8 *buf, bool_Bool is_json, enum eDriveModel eModel)
1566{
1567 struct json_object *root = NULL((void*)0);
1568 struct json_object *logPages = NULL((void*)0);
1569 struct json_object *stats = NULL((void*)0);
1570 int field_count = 0;
1571
1572 if (eModel == M51CX)
1573 field_count = ARRAY_SIZE(ocp_c0_log_page)(sizeof(ocp_c0_log_page) / sizeof((ocp_c0_log_page)[0]));
1574 else if (eModel == M51BY || eModel == M51CY)
1575 field_count = ARRAY_SIZE(datacenter_c0_log_page)(sizeof(datacenter_c0_log_page) / sizeof((datacenter_c0_log_page
)[0]))
;
1576
1577 if (is_json) {
1578 root = json_create_object()json_object_new_object();
1579 stats = json_create_object()json_object_new_object();
1580 logPages = json_create_array()json_object_new_array();
1581 if (eModel == M51BY || eModel == M51CY)
1582 json_object_add_value_array(root, "OCP DataCenter SMART Health Log: 0xC0",json_object_object_add(root, "OCP DataCenter SMART Health Log: 0xC0"
, logPages)
1583 logPages)json_object_object_add(root, "OCP DataCenter SMART Health Log: 0xC0"
, logPages)
;
1584 else if (eModel == M51CX)
1585 json_object_add_value_array(root, "OCP SMART Cloud Health Log: 0xC0",json_object_object_add(root, "OCP SMART Cloud Health Log: 0xC0"
, logPages)
1586 logPages)json_object_object_add(root, "OCP SMART Cloud Health Log: 0xC0"
, logPages)
;
1587 }
1588
1589 if (eModel == M51BY || eModel == M51CY)
1590 generic_structure_parser(buf, datacenter_c0_log_page, field_count, stats, 0, NULL((void*)0));
1591 else if (eModel == M51CX)
1592 generic_structure_parser(buf, ocp_c0_log_page, field_count, stats, 0, NULL((void*)0));
1593
1594 if (is_json) {
1595 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1596 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1597 printf("\n");
1598 json_free_object(root)json_object_put(root);
1599 }
1600}
1601
1602static void print_hyperscale_cloud_health_log(__u8 *buf, bool_Bool is_json)
1603{
1604 struct json_object *root;
1605 struct json_object *logPages;
1606 struct json_object *stats = NULL((void*)0);
1607 int field_count = ARRAY_SIZE(hyperscale_c0_log_page)(sizeof(hyperscale_c0_log_page) / sizeof((hyperscale_c0_log_page
)[0]))
;
1608
1609 if (is_json) {
1610 root = json_create_object()json_object_new_object();
1611 stats = json_create_object()json_object_new_object();
1612 logPages = json_create_array()json_object_new_array();
1613 json_object_add_value_array(root, "OCP Hyperscale Cloud Health Log: 0xC0",json_object_object_add(root, "OCP Hyperscale Cloud Health Log: 0xC0"
, logPages)
1614 logPages)json_object_object_add(root, "OCP Hyperscale Cloud Health Log: 0xC0"
, logPages)
;
1615 }
1616
1617 generic_structure_parser(buf, hyperscale_c0_log_page, field_count, stats, 0, NULL((void*)0));
1618
1619 if (is_json) {
1620 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1621 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1622 printf("\n");
1623 json_free_object(root)json_object_put(root);
1624 }
1625}
1626
1627static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool_Bool is_json, __u8 spec)
1628{
1629 struct json_object *root;
1630 struct json_object *logPages;
1631 struct json_object *stats = NULL((void*)0);
1632 int field_count = ARRAY_SIZE(fb_log_page)(sizeof(fb_log_page) / sizeof((fb_log_page)[0]));
1633
1634 if (is_json) {
1635 root = json_create_object()json_object_new_object();
1636 stats = json_create_object()json_object_new_object();
1637 logPages = json_create_array()json_object_new_array();
1638 json_object_add_value_array(root, "Extended Smart Log Page : 0xFB",json_object_object_add(root, "Extended Smart Log Page : 0xFB"
, logPages)
1639 logPages)json_object_object_add(root, "Extended Smart Log Page : 0xFB"
, logPages)
;
1640 }
1641
1642 generic_structure_parser(buf, fb_log_page, field_count, stats, spec, NULL((void*)0));
1643
1644 /* print last three entries from D0 log page */
1645 if (buf2) {
1646 init_d0_log_page(buf2, nsze);
1647
1648 if (is_json) {
1649 for (int i = 0; i < 7; i++)
1650 json_object_add_value_string(stats,
1651 d0_log_page[i].field,
1652 d0_log_page[i].datastr);
1653 } else {
1654 for (int i = 0; i < 7; i++)
1655 printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr);
1656 }
1657 }
1658
1659 if (is_json) {
1660 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1661 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1662 printf("\n");
1663 json_free_object(root)json_object_put(root);
1664 }
1665}
1666
1667static void print_nand_stats_d0(__u8 *buf, __u8 oacs, bool_Bool is_json)
1668{
1669 init_d0_log_page(buf, oacs);
1670
1671 if (is_json) {
1672 struct json_object *root = json_create_object()json_object_new_object();
1673 struct json_object *stats = json_create_object()json_object_new_object();
1674 struct json_object *logPages = json_create_array()json_object_new_array();
1675
1676 json_object_add_value_array(root, "Extended Smart Log Page : 0xD0", logPages)json_object_object_add(root, "Extended Smart Log Page : 0xD0"
, logPages)
;
1677
1678 for (int i = 0; i < 7; i++)
1679 json_object_add_value_string(stats, d0_log_page[i].field,
1680 d0_log_page[i].datastr);
1681
1682 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1683 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1684 printf("\n");
1685 json_free_object(root)json_object_put(root);
1686 } else {
1687 for (int i = 0; i < 7; i++)
1688 printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr);
1689 }
1690}
1691
1692static void print_hyperscale_nand_stats(__u8 *buf, bool_Bool is_json)
1693{
1694 init_hyperscale_BSSD_nand_stats(buf);
1695
1696 if (is_json) {
1697 struct json_object *root = json_create_object()json_object_new_object();
1698 struct json_object *stats = json_create_object()json_object_new_object();
1699 struct json_object *logPages = json_create_array()json_object_new_array();
1700
1701 json_object_add_value_array(root, "Extended Smart Log Page : 0xC0", logPages)json_object_object_add(root, "Extended Smart Log Page : 0xC0"
, logPages)
;
1702
1703 for (int i = 0; i < 22; i++)
1704 json_object_add_value_string(stats,
1705 hyperscale_BSSD_nand_stats[i].field,
1706 hyperscale_BSSD_nand_stats[i].datastr);
1707
1708 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1709 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1710 printf("\n");
1711 json_free_object(root)json_object_put(root);
1712 } else {
1713 for (int i = 0; i < 22; i++)
1714 printf("%-40s : %s\n", hyperscale_BSSD_nand_stats[i].field,
1715 hyperscale_BSSD_nand_stats[i].datastr);
1716 }
1717}
1718
1719static bool_Bool nsze_from_oacs;/* read nsze for now from idd[4059] */
1720
1721static int micron_nand_stats(int argc, char **argv,
1722 struct command *command, struct plugin *plugin)
1723{
1724 const char *desc = "Retrieve Micron NAND stats for the given device ";
1725 unsigned int extSmartLog[D0_log_size512/sizeof(int)] = { 0 };
1726 unsigned int logFB[FB_log_size512/sizeof(int)] = { 0 };
1727 unsigned char logC0[C0_log_size512] = { 0 };
1728 enum eDriveModel eModel = UNKNOWN_MODEL;
1729 struct nvme_id_ctrl ctrl;
1730 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1731 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1732 int err;
1733 __u8 nsze;
1734 bool_Bool has_d0_log = true1;
1735 bool_Bool has_fb_log = false0;
1736 bool_Bool is_json = true1;
1737 nsze_from_oacs = false0;
1738 struct format {
1739 char *fmt;
1740 };
1741 const char *fmt = "output format json|normal";
1742 struct format cfg = {
1743 .fmt = "json",
1744 };
1745
1746 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
1747 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
1748
1749 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
1750 if (err) {
1751 printf("\nDevice not found\n");
1752 return -1;
1753 }
1754
1755 if (!strcmp(cfg.fmt, "normal"))
1756 is_json = false0;
1757
1758 /* pull log details based on the model name */
1759 eModel = GetDriveModel(ctx, hdl);
1760 if (eModel == UNKNOWN_MODEL) {
1761 printf("Unsupported drive model for vs-nand-stats command\n");
1762 return -1;
1763 }
1764
1765 err = nvme_identify_ctrl(hdl, &ctrl);
1766 if (err) {
1767 nvme_show_err(err, "ERROR : identify_ctrl() failed");
1768 return -1;
1769 }
1770
1771 if ((ctrl.vs[536] == MICRON_CUST_ID_GG0x16) && (eModel == M51CX)) {
1772 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
1773 if (err == 0) {
1774 print_hyperscale_nand_stats((__u8 *)logC0, is_json);
1775 goto out;
1776 } else if (err < 0) {
1777 nvme_show_err(err, "Unable to retrieve extended smart log 0xC0 for the drive");
1778 return -1;
1779 }
1780 }
1781
1782 err = nvme_get_log_simple(hdl, 0xD0, extSmartLog, D0_log_size512);
1783 has_d0_log = !err;
1784
1785 /* should check for firmware version if this log is supported or not */
1786 if (eModel != M5407 && eModel != M5410) {
1787 err = nvme_get_log_simple(hdl, 0xFB, logFB, FB_log_size512);
1788 has_fb_log = !err;
1789 }
1790
1791 nsze = (ctrl.vs[987] == 0x12);
1792 if (!nsze && nsze_from_oacs)
1793 nsze = ((ctrl.oacs >> 3) & 0x1);
1794
1795 if (has_fb_log) {
1796 __u8 spec = (eModel == M5410) ? 0 : 1; /* FB spec version */
1797
1798 print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json, spec);
1799 err = 0;
1800 } else if (has_d0_log) {
1801 print_nand_stats_d0((__u8 *)extSmartLog, nsze, is_json);
1802 err = 0;
1803 }
1804out:
1805 if (err)
1806 nvme_show_err(err, "Unable to retrieve extended smart log for the drive");
1807
1808 return err;
1809}
1810
1811static void print_log(__u8 *buf, bool_Bool is_json, unsigned char ucLogID)
1812{
1813 struct json_object *root;
1814 struct json_object *logPages;
1815 struct json_object *stats = NULL((void*)0);
1816 int field_count = 0;
1817 struct request_data *log_pageID;
1818 char tempStr[256] = { 0 };
1819
1820 if (ucLogID == 0xE1) {
1821 log_pageID = e1_log_page;
1822 field_count = ARRAY_SIZE(e1_log_page)(sizeof(e1_log_page) / sizeof((e1_log_page)[0]));
1823 sprintf(tempStr, "SMART Extended Log:0x%X", ucLogID);
1824 } else if (ucLogID == 0xD0) {
1825 log_pageID = D0_log_page;
1826 field_count = ARRAY_SIZE(D0_log_page)(sizeof(D0_log_page) / sizeof((D0_log_page)[0]));
1827 sprintf(tempStr, "SMART Extended Log:0x%X", ucLogID);
1828 } else if (ucLogID == 0xC5) {
1829 log_pageID = C5_log_page;
1830 field_count = ARRAY_SIZE(C5_log_page)(sizeof(C5_log_page) / sizeof((C5_log_page)[0]));
1831 sprintf(tempStr, "Micron Workload Log:0x%X", ucLogID);
1832 } else if (ucLogID == 0xC6) {
1833 log_pageID = C6_log_page;
1834 field_count = ARRAY_SIZE(C6_log_page)(sizeof(C6_log_page) / sizeof((C6_log_page)[0]));
1835 sprintf(tempStr, "Vendor Telemetry Log:0x%X", ucLogID);
1836 } else {
1837 log_pageID = NULL((void*)0);
1838 }
1839
1840 if (is_json) {
1841 root = json_create_object()json_object_new_object();
1842 stats = json_create_object()json_object_new_object();
1843 logPages = json_create_array()json_object_new_array();
1844 json_object_add_value_array(root, tempStr, logPages)json_object_object_add(root, tempStr, logPages);
1845 } else {
1846 printf("%s\n", tempStr);
1847 }
1848
1849 if (log_pageID != NULL((void*)0))
1850 generic_structure_parser(buf, log_pageID, field_count, stats, 0, NULL((void*)0));
1851
1852 if (is_json) {
1853 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1854 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1855 printf("\n");
1856 json_free_object(root)json_object_put(root);
1857 }
1858}
1859
1860static int micron_smart_ext_log(int argc, char **argv,
1861 struct command *command, struct plugin *plugin)
1862{
1863 const char *desc = "Retrieve extended SMART logs for the given device ";
1864 unsigned int extSmartLog[E1_log_size256/sizeof(int)] = { 0 };
1865 enum eDriveModel eModel = UNKNOWN_MODEL;
1866 int err = 0;
1867 __u8 log_id;
1868 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1869 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1870 bool_Bool is_json = true1;
1871 struct format {
1872 char *fmt;
1873 };
1874 const char *fmt = "output format json|normal";
1875 struct format cfg = {
1876 .fmt = "json",
1877 };
1878 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
1879 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
1880
1881 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
1882 if (err) {
1883 printf("\nDevice not found\n");
1884 return -1;
1885 }
1886 if (!strcmp(cfg.fmt, "normal"))
1887 is_json = false0;
1888
1889 eModel = GetDriveModel(ctx, hdl);
1890 if (eModel == M51CX || eModel == M51BY || eModel == M51CY || eModel == M6003 ||
1891 eModel == M6004) {
1892 log_id = 0xE1;
1893 } else if (eModel == M6001) {
1894 log_id = 0xD0;
1895 } else {
1896 printf("Unsupported drive model for vs-smart-ext-log command\n");
1897 err = -1;
1898 goto out;
1899 }
1900 err = nvme_get_log_simple(hdl, log_id, extSmartLog, E1_log_size256);
1901 if (!err)
1902 print_log((__u8 *)extSmartLog, is_json, log_id);
1903
1904out:
1905 if (err > 0)
1906 nvme_show_status(err);
1907 return err;
1908}
1909
1910static int micron_work_load_log(int argc, char **argv, struct command *acmd, struct plugin *plugin)
1911{
1912 const char *desc = "Retrieve Micron Workload logs for the given device ";
1913 unsigned int micronWorkLoadLog[C5_MicronWorkLoad_log_size256/sizeof(int)] = { 0 };
1914 enum eDriveModel eModel = UNKNOWN_MODEL;
1915 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1916 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1917
1918 int err = 0;
1919 bool_Bool is_json = true1;
1920 struct format {
1921 char *fmt;
1922 };
1923 const char *fmt = "output format json|normal";
1924 struct format cfg = {
1925 .fmt = "json",
1926 };
1927 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
1928 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
1929
1930 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
1931 if (err) {
1932 printf("\nDevice not found\n");
1933 return -1;
1934 }
1935 if (strcmp(cfg.fmt, "normal") == 0)
1936 is_json = false0;
1937
1938 eModel = GetDriveModel(ctx, hdl);
1939 if (eModel == M6001 || eModel == M6004 || eModel == M6003) {
1940 err = nvme_get_log_simple(hdl, 0xC5,
1941 micronWorkLoadLog, C5_MicronWorkLoad_log_size256);
1942 if (!err)
1943 print_log((__u8 *)micronWorkLoadLog, is_json, 0xC5);
1944 } else {
1945 printf("Unsupported drive model for vs-work-load-log command\n");
1946 err = -1;
1947 goto out;
1948 }
1949
1950out:
1951 if (err > 0)
1952 nvme_show_status(err);
1953 return err;
1954}
1955
1956static int micron_vendor_telemetry_log(int argc, char **argv,
1957 struct command *command, struct plugin *plugin)
1958{
1959 const char *desc = "Retrieve Vendor Telemetry logs for the given device ";
1960 unsigned int vendorTelemetryLog[C6_log_size512/sizeof(int)] = { 0 };
1961 enum eDriveModel eModel = UNKNOWN_MODEL;
1962 int err = 0;
1963 bool_Bool is_json = true1;
1964 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1965 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1966
1967 struct format {
1968 char *fmt;
1969 };
1970 const char *fmt = "output format json|normal";
1971 struct format cfg = {
1972 .fmt = "json",
1973 };
1974 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
1975 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
1976
1977 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
1978 if (err) {
1979 printf("\nDevice not found\n");
1980 return -1;
1981 }
1982 if (strcmp(cfg.fmt, "normal") == 0)
1983 is_json = false0;
1984
1985 eModel = GetDriveModel(ctx, hdl);
1986 if (eModel == M6001 || eModel == M6004 || eModel == M6003) {
1987 err = nvme_get_log_simple(hdl, 0xC6, vendorTelemetryLog, C6_log_size512);
1988 if (!err)
1989 print_log((__u8 *)vendorTelemetryLog, is_json, 0xC6);
1990 } else {
1991 printf("Unsupported drive model for vs-vendor-telemetry-log command\n");
1992 err = -1;
1993 goto out;
1994 }
1995
1996out:
1997 if (err > 0)
1998 nvme_show_status(err);
1999 return err;
2000}
2001
2002static void GetDriveInfo(const char *strOSDirName, int nFD,
2003 struct nvme_id_ctrl *ctrlp)
2004{
2005 FILE *fpOutFile = NULL((void*)0);
2006 char tempFile[256] = { 0 };
2007 char strBuffer[1024] = { 0 };
2008 char model[41] = { 0 };
2009 char serial[21] = { 0 };
2010 char fwrev[9] = { 0 };
2011 char *strPDir = strdup(strOSDirName);
2012 char *strDest = dirname(strPDir);
2013
2014 sprintf(tempFile, "%s/%s", strDest, "drive-info.txt");
2015 fpOutFile = fopen(tempFile, "w+");
2016 if (!fpOutFile) {
2017 printf("Failed to create %s\n", tempFile);
2018 free(strPDir);
2019 return;
2020 }
2021
2022 strncpy(model, ctrlp->mn, 40);
2023 strncpy(serial, ctrlp->sn, 20);
2024 strncpy(fwrev, ctrlp->fr, 8);
2025
2026 sprintf(strBuffer,
2027 "********************\nDrive Info\n********************\n");
2028
2029 fprintf(fpOutFile, "%s", strBuffer);
2030 sprintf(strBuffer,
2031 "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n",
2032 "Device Name", nFD,
2033 "Model No", (char *)model,
2034 "Serial No", (char *)serial, "FW-Rev", (char *)fwrev);
2035
2036 fprintf(fpOutFile, "%s", strBuffer);
2037
2038 sprintf(strBuffer,
2039 "\n********************\nPCI Info\n********************\n");
2040
2041 fprintf(fpOutFile, "%s", strBuffer);
2042
2043 sprintf(strBuffer,
2044 "%-22s : %04X\n%-22s : %04X\n",
2045 "VendorId", vendor_id, "DeviceId", device_id);
2046 fprintf(fpOutFile, "%s", strBuffer);
2047 fclose(fpOutFile);
2048 free(strPDir);
2049}
2050
2051static void GetTimestampInfo(const char *strOSDirName)
2052{
2053 __u8 outstr[1024];
2054 time_t t;
2055 struct tm *tmp;
2056 size_t num;
2057 char *strPDir;
2058 char *strDest;
2059
2060 t = time(NULL((void*)0));
2061 tmp = localtime(&t);
2062 if (!tmp)
2063 return;
2064
2065 num = strftime((char *)outstr, sizeof(outstr),
2066 "Timestamp (UTC): %a, %d %b %Y %H:%M:%S %z", tmp);
2067 num += sprintf((char *)(outstr + num), "\nPackage Version: 1.4");
2068 if (num) {
2069 strPDir = strdup(strOSDirName);
2070 strDest = dirname(strPDir);
2071 WriteData(outstr, num, strDest, "timestamp_info.txt", "timestamp");
2072 free(strPDir);
2073 }
2074}
2075
2076static void GetCtrlIDDInfo(const char *dir, struct nvme_id_ctrl *ctrlp)
2077{
2078 WriteData((__u8 *)ctrlp, sizeof(*ctrlp), dir,
2079 "nvme_controller_identify_data.bin", "id-ctrl");
2080}
2081
2082static void GetSmartlogData(struct libnvme_transport_handle *hdl, const char *dir)
2083{
2084 struct nvme_smart_log smart_log;
2085
2086 if (!nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log))
2087 WriteData((__u8 *)&smart_log, sizeof(smart_log), dir,
2088 "smart_data.bin", "smart log");
2089}
2090
2091static void GetErrorlogData(struct libnvme_transport_handle *hdl, int entries, const char *dir)
2092{
2093 int logSize = entries * sizeof(struct nvme_error_log_page);
2094 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_error_log_page *error_log =
2095 (struct nvme_error_log_page *)libnvme_alloc(logSize);
2096
2097 if (!error_log)
2098 return;
2099
2100 if (!nvme_get_log_error(hdl, NVME_NSID_ALL, entries, error_log))
2101 WriteData((__u8 *)error_log, logSize, dir,
2102 "error_information_log.bin", "error log");
2103}
2104
2105static void GetGenericLogs(struct libnvme_transport_handle *hdl, const char *dir)
2106{
2107 struct nvme_self_test_log self_test_log;
2108 struct nvme_firmware_slot fw_log;
2109 struct nvme_cmd_effects_log effects;
2110 struct nvme_persistent_event_log pevent_log;
2111 __cleanup_huge__attribute__((cleanup(libnvme_free_huge))) struct libnvme_mem_huge mh = { 0, };
2112 void *pevent_log_info = NULL((void*)0);
2113 __u32 log_len = 0;
2114 int err = 0;
2115
2116 /* get self test log */
2117 if (!nvme_get_log_device_self_test(hdl, &self_test_log))
2118 WriteData((__u8 *)&self_test_log, sizeof(self_test_log), dir,
2119 "drive_self_test.bin", "self test log");
2120
2121 /* get fw slot info log */
2122 if (!nvme_get_log_fw_slot(hdl, false0, &fw_log))
2123 WriteData((__u8 *)&fw_log, sizeof(fw_log), dir,
2124 "firmware_slot_info_log.bin", "firmware log");
2125
2126 /* get effects log */
2127 if (!nvme_get_log_cmd_effects(hdl, NVME_CSI_NVM, &effects))
2128 WriteData((__u8 *)&effects, sizeof(effects), dir,
2129 "command_effects_log.bin", "effects log");
2130
2131 /* get persistent event log */
2132 (void)nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_RELEASE_CTX,
2133 &pevent_log, sizeof(pevent_log));
2134 memset(&pevent_log, 0, sizeof(pevent_log));
2135 err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_EST_CTX_AND_READ,
2136 &pevent_log, sizeof(pevent_log));
2137 if (err) {
2138 fprintf(stderrstderr, "Setting persistent event log read ctx failed (ignored)!\n");
2139 return;
2140 }
2141
2142 log_len = le64_to_cpu(pevent_log.tll);
2143 pevent_log_info = libnvme_alloc_huge(log_len, &mh);
2144 if (!pevent_log_info) {
2145 perror("could not alloc buffer for persistent event log page (ignored)!\n");
2146 return;
2147 }
2148
2149 err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_READ,
2150 pevent_log_info, log_len);
2151 if (!err)
2152 WriteData((__u8 *)pevent_log_info, log_len, dir,
2153 "persistent_event_log.bin", "persistent event log");
2154}
2155
2156static void GetNSIDDInfo(struct libnvme_transport_handle *hdl, const char *dir, int nsid)
2157{
2158 char file[PATH_MAX4096] = { 0 };
2159 struct nvme_id_ns ns;
2160
2161 if (!nvme_identify_ns(hdl, nsid, &ns)) {
2162 sprintf(file, "identify_namespace_%d_data.bin", nsid);
2163 WriteData((__u8 *)&ns, sizeof(ns), dir, file, "id-ns");
2164 }
2165}
2166
2167static void GetOSConfig(const char *strOSDirName)
2168{
2169 char strFileName[4096];
2170 sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt");
2171 micron_write_os_config_to_file(strFileName);
2172}
2173
2174static int micron_telemetry_log(struct libnvme_transport_handle *hdl, __u8 type, __u8 **data,
2175 int *logSize, int da)
2176{
2177 int err, bs = 512, offset = bs;
2178 unsigned short data_area[5] = { 0 };
2179 unsigned char ctrl_init = (type == 0x8);
2180
2181 __u8 *buffer = (unsigned char *)libnvme_alloc(bs);
2182
2183 if (!buffer)
2184 return -1;
2185 if (ctrl_init)
2186 err = nvme_get_log_telemetry_ctrl(hdl, true1, 0, buffer, bs);
2187 else
2188 err = nvme_get_log_telemetry_host(hdl, 0, buffer, bs);
2189 if (err) {
2190 fprintf(stderrstderr, "Failed to get telemetry log header for 0x%X\n", type);
2191 libnvme_free(buffer);
2192 return err;
2193 }
2194
2195 /* compute size of the log */
2196 data_area[1] = buffer[9] << 8 | buffer[8];
2197 data_area[2] = buffer[11] << 8 | buffer[10];
2198 data_area[3] = buffer[13] << 8 | buffer[12];
2199 data_area[4] = buffer[15] << 8 | buffer[14];
2200 data_area[0] = data_area[1] > data_area[2] ? data_area[1] : data_area[2];
2201 data_area[0] = data_area[3] > data_area[0] ? data_area[3] : data_area[0];
2202 data_area[0] = data_area[4] > data_area[0] ? data_area[4] : data_area[0];
2203
2204 if (!data_area[da]) {
2205 fprintf(stderrstderr, "Requested telemetry data for 0x%X is empty\n", type);
2206 libnvme_free(buffer);
2207 buffer = NULL((void*)0);
2208 return -1;
2209 }
2210
2211 *logSize = data_area[da] * bs;
2212 offset = bs;
2213 err = 0;
2214 buffer = (unsigned char *)libnvme_realloc(buffer, (size_t)(*logSize));
2215 if (buffer) {
2216 while (!err && offset != *logSize) {
2217 if (ctrl_init)
2218 err = nvme_get_log_telemetry_ctrl(hdl, true1, offset, buffer + offset, bs);
2219 else
2220 err = nvme_get_log_telemetry_host(hdl, offset, buffer + offset, bs);
2221 offset += bs;
2222 }
2223 }
2224
2225 if (!err && buffer) {
2226 *data = buffer;
2227 } else {
2228 nvme_show_err(err, "Failed to get telemetry data for 0x%x\n", type);
2229 libnvme_free(buffer);
2230 }
2231
2232 return err;
2233}
2234
2235static int GetTelemetryData(struct libnvme_transport_handle *hdl, const char *dir)
2236{
2237 unsigned char *buffer = NULL((void*)0);
2238 int i, err, logSize = 0;
2239 char msg[256] = { 0 };
2240 struct {
2241 __u8 log;
2242 char *file;
2243 } tmap[] = {
2244 {0x07, "nvmetelemetrylog.bin"},
2245 {0x08, "nvmetelemetrylog.bin"},
2246 };
2247
2248 for (i = 0; i < (int)(ARRAY_SIZE(tmap)(sizeof(tmap) / sizeof((tmap)[0]))); i++) {
2249 err = micron_telemetry_log(hdl, tmap[i].log, &buffer, &logSize, 0);
2250 if (!err && logSize > 0 && buffer) {
2251 sprintf(msg, "telemetry log: 0x%X", tmap[i].log);
2252 WriteData(buffer, logSize, dir, tmap[i].file, msg);
2253 }
2254 libnvme_free(buffer);
2255 buffer = NULL((void*)0);
2256 logSize = 0;
2257 }
2258 return err;
2259}
2260
2261static int GetFeatureSettings(struct libnvme_transport_handle *hdl, const char *dir)
2262{
2263 unsigned char *bufp, buf[4096] = { 0 };
2264 int i, err, len, errcnt = 0;
2265 __u64 attrVal = 0;
2266 char msg[256] = { 0 };
2267
2268 struct features {
2269 int id;
2270 char *file;
2271 } fmap[] = {
2272 {0x01, "nvme_feature_setting_arbitration.bin"},
2273 {0x02, "nvme_feature_setting_pm.bin"},
2274 {0x03, "nvme_feature_setting_lba_range_namespace_1.bin"},
2275 {0x04, "nvme_feature_setting_temp_threshold.bin"},
2276 {0x05, "nvme_feature_setting_error_recovery.bin"},
2277 {0x06, "nvme_feature_setting_volatile_write_cache.bin"},
2278 {0x07, "nvme_feature_setting_num_queues.bin"},
2279 {0x08, "nvme_feature_setting_interrupt_coalescing.bin"},
2280 {0x09, "nvme_feature_setting_interrupt_vec_config.bin"},
2281 {0x0A, "nvme_feature_setting_write_atomicity.bin"},
2282 {0x0B, "nvme_feature_setting_async_event_config.bin"},
2283 {0x80, "nvme_feature_setting_sw_progress_marker.bin"},
2284 };
2285
2286 for (i = 0; i < (int)(ARRAY_SIZE(fmap)(sizeof(fmap) / sizeof((fmap)[0]))); i++) {
2287 if (fmap[i].id == 0x03) {
2288 len = 4096;
2289 bufp = (unsigned char *)(&buf[0]);
2290 } else {
2291 len = 0;
2292 bufp = NULL((void*)0);
2293 }
2294 err = nvme_get_features(hdl, 1, fmap[i].id, 0, 0x0, 0, bufp, len,
2295 &attrVal);
2296 if (!err) {
2297 sprintf(msg, "feature: 0x%X", fmap[i].id);
2298 WriteData((__u8 *)&attrVal, sizeof(attrVal), dir, fmap[i].file, msg);
2299 if (bufp)
2300 WriteData(bufp, len, dir, fmap[i].file, msg);
2301 } else {
2302 fprintf(stderrstderr, "Feature 0x%x data not retrieved, error %d (ignored)!\n",
2303 fmap[i].id, err);
2304 errcnt++;
2305 }
2306 }
2307 return (int)(errcnt == ARRAY_SIZE(fmap)(sizeof(fmap) / sizeof((fmap)[0])));
2308}
2309
2310static int micron_drive_info(int argc, char **argv, struct command *acmd,
2311 struct plugin *plugin)
2312{
2313 const char *desc = "Get drive HW information";
2314 struct nvme_id_ctrl ctrl = { 0 };
2315 struct libnvme_passthru_cmd admin_cmd = { 0 };
2316 unsigned char logC0[C0_log_size512] = { 0 };
2317 struct fb_drive_info {
2318 unsigned char hw_ver_major;
2319 unsigned char hw_ver_minor;
2320 unsigned char ftl_unit_size;
2321 unsigned short bs_ver_major;
2322 unsigned short bs_ver_minor;
2323 unsigned int ownership_status;
2324 } dinfo = { 0 };
2325 enum eDriveModel model = UNKNOWN_MODEL;
2326 __u8 custId = 0x10; /* default Micron generic */
2327 bool_Bool is_json = false0;
2328 struct json_object *root;
2329 struct json_object *driveInfo;
2330 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
2331 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
2332 struct format {
2333 char *fmt;
2334 };
2335 int err = 0;
2336
2337 const char *fmt = "output format normal|json";
2338 struct format cfg = {
2339 .fmt = "normal",
2340 };
2341
2342 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
2343 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
2344
2345 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
2346 if (err < 0)
2347 return err;
2348
2349 if (model == UNKNOWN_MODEL) {
2350 fprintf(stderrstderr, "ERROR : Unsupported drive for vs-drive-info cmd");
2351 return -1;
2352 }
2353
2354 if (strcmp(cfg.fmt, "normal") && strcmp(cfg.fmt, "json")) {
2355 fprintf(stderrstderr, "Invalid output format\n");
2356 return -1;
2357 }
2358
2359 if (!strcmp(cfg.fmt, "json"))
2360 is_json = true1;
2361
2362 if (model == M5407) {
2363 admin_cmd.opcode = 0xDA;
2364 admin_cmd.addr = (__u64) (uintptr_t) &dinfo;
2365 admin_cmd.data_len = (__u32)sizeof(dinfo);
2366 admin_cmd.cdw12 = 3;
2367 err = libnvme_exec_admin_passthru(hdl, &admin_cmd);
2368 if (err) {
2369 fprintf(stderrstderr, "ERROR : drive-info opcode failed with 0x%x\n", err);
2370 return -1;
2371 }
2372 } else {
2373 err = nvme_identify_ctrl(hdl, &ctrl);
2374 if (err) {
2375 fprintf(stderrstderr, "ERROR : identify_ctrl() failed with 0x%x\n", err);
2376 return -1;
2377 }
2378 dinfo.hw_ver_major = ctrl.vs[820];
2379 dinfo.hw_ver_minor = ctrl.vs[821];
2380 dinfo.ftl_unit_size = ctrl.vs[822];
2381 custId = ctrl.vs[536];
2382 }
2383
2384 if ((custId == MICRON_CUST_ID_GG0x16) && (model == M51CX)) {
2385 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
2386 if (err == 0) {
2387 dinfo.bs_ver_major = *((__u16 *)(logC0+300));
2388 dinfo.bs_ver_minor = *((__u16 *)(logC0+302));
2389 dinfo.ownership_status = *((__u32 *)(logC0+312));
2390 } else {
2391 nvme_show_err(err, "Unable to retrieve extended smart log 0xC0 for the drive");
2392 return -1;
2393 }
2394 }
2395
2396 if (is_json) {
2397 struct json_object *pinfo = json_create_object()json_object_new_object();
2398 char tempstr[64] = { 0 };
2399 root = json_create_object()json_object_new_object();
2400 driveInfo = json_create_array()json_object_new_array();
2401 json_object_add_value_array(root, "Micron Drive HW Information", driveInfo)json_object_object_add(root, "Micron Drive HW Information", driveInfo
)
;
2402
2403 sprintf(tempstr, "%hhu.%hhu", dinfo.hw_ver_major, dinfo.hw_ver_minor);
2404 json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr);
2405
2406 if (custId == MICRON_CUST_ID_GG0x16) {
2407 if (dinfo.ftl_unit_size) {
2408 sprintf(tempstr, "%u B",
2409 (unsigned int)(dinfo.ftl_unit_size * 1024));
2410 json_object_add_value_string(pinfo, "FTL_unit_size", tempstr);
2411 }
2412
2413 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
2414 sprintf(tempstr,
2415 "HyperScale Boot Version Spec.%hhu.%hhu"
2416 , (unsigned char)dinfo.bs_ver_major,
2417 (unsigned char)dinfo.bs_ver_minor);
2418 json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr);
2419 }
2420
2421 if (dinfo.ownership_status == 0)
2422 sprintf(tempstr, "N/A");
2423 else if (dinfo.ownership_status == 1)
2424 sprintf(tempstr, "UNSET");
2425 else if (dinfo.ownership_status == 2)
2426 sprintf(tempstr, "SET");
2427 else if (dinfo.ownership_status == 3)
2428 sprintf(tempstr, "BLOCKED");
2429 json_object_add_value_string(pinfo, "Drive Ownership Status", tempstr);
2430
2431 } else {
2432 if (dinfo.ftl_unit_size) {
2433 sprintf(tempstr, "%hhu KB", dinfo.ftl_unit_size);
2434 json_object_add_value_string(pinfo, "FTL_unit_size", tempstr);
2435 }
2436 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
2437 sprintf(tempstr, "%hhu.%hhu", (unsigned char)dinfo.bs_ver_major,
2438 (unsigned char)dinfo.bs_ver_minor);
2439 json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr);
2440 }
2441 }
2442 json_array_add_value_object(driveInfo, pinfo)json_object_array_add(driveInfo, pinfo);
2443 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
2444 printf("\n");
2445 json_free_object(root)json_object_put(root);
2446 } else {
2447 printf("Drive Hardware Version: %hhu.%hhu\n",
2448 dinfo.hw_ver_major, dinfo.hw_ver_minor);
2449
2450 if (custId == MICRON_CUST_ID_GG0x16) {
2451 if (dinfo.ftl_unit_size)
2452 printf("FTL_unit_size: %u B\n",
2453 (unsigned int)(dinfo.ftl_unit_size * 1024));
2454
2455 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
2456 printf(
2457 "Boot Spec.Version: HyperScale Boot Version Spec.%hhu.%hhu\n"
2458 , (unsigned char)dinfo.bs_ver_major,
2459 (unsigned char)dinfo.bs_ver_minor);
2460 }
2461
2462 if (dinfo.ownership_status == 0)
2463 printf("Drive Ownership Status: N/A\n");
2464 else if (dinfo.ownership_status == 1)
2465 printf("Drive Ownership Status: UNSET\n");
2466 else if (dinfo.ownership_status == 2)
2467 printf("Drive Ownership Status: SET\n");
2468 else if (dinfo.ownership_status == 3)
2469 printf("Drive Ownership Status: BLOCKED\n");
2470
2471 } else {
2472 if (dinfo.ftl_unit_size)
2473 printf("FTL_unit_size: %hhu KB\n", dinfo.ftl_unit_size);
2474 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0)
2475 printf(
2476 "Boot Spec.Version: %hhu.%hhu\n"
2477 , (unsigned char)dinfo.bs_ver_major,
2478 (unsigned char)dinfo.bs_ver_minor);
2479 }
2480
2481 }
2482
2483 return 0;
2484}
2485
2486static int micron_cloud_ssd_plugin_version(int argc, char **argv,
2487 struct command *command, struct plugin *plugin)
2488{
2489 printf("nvme-cli Micron cloud SSD plugin version: %s.%s\n",
2490 __version_major, __version_minor);
2491 return 0;
2492}
2493
2494static int micron_plugin_version(int argc, char **argv, struct command *acmd,
2495 struct plugin *plugin)
2496{
2497 printf("nvme-cli Micron plugin version: %s.%s.%s\n",
2498 __version_major, __version_minor, __version_patch);
2499 return 0;
2500}
2501
2502/* Binary format of firmware activation history entry */
2503struct __packed__attribute__((__packed__)) fw_activation_history_entry {
2504 __u8 version;
2505 __u8 length;
2506 __u16 rsvd1;
2507 __le16 valid;
2508 __le64 power_on_hour;
2509 __le64 rsvd2;
2510 __le64 power_cycle_count;
2511 __u8 previous_fw[8];
2512 __u8 activated_fw[8];
2513 __u8 slot;
2514 __u8 commit_action_type;
2515 __le16 result;
2516 __u8 rsvd3[14];
2517};
2518
2519/* Binary format for firmware activation history table */
2520struct __packed__attribute__((__packed__)) micron_fw_activation_history_table {
2521 __u8 log_page;
2522 __u8 rsvd1[3];
2523 __le32 num_entries;
2524 struct fw_activation_history_entry entries[20];
2525 __u8 rsvd2[2790];
2526 __u16 version;
2527 __u8 GUID[16];
2528};
2529
2530static int display_fw_activate_entry(int entry_count, struct fw_activation_history_entry *entry,
2531 char *formatted_entry, struct json_object *stats)
2532{
2533 time_t timestamp, hours;
2534 char buffer[32];
2535 __u8 minutes, seconds;
2536 static const char * const ca[] = {"000b", "001b", "010b", "011b"};
2537 char *ptr = formatted_entry;
2538 int index = 0, entry_size = 82;
2539 bool_Bool is_json = false0;
2540
2541 if ((entry->version != 1 && entry->version != 2) || entry->length != 64)
2542 return -EINVAL22;
2543
2544 if (stats)
2545 is_json = true1;
2546
2547 sprintf(ptr, "%d", entry_count);
2548 if (is_json)
2549 json_object_add_value_int(stats, "Entry Number", le32_to_cpu(entry_count))json_object_object_add(stats, "Entry Number", json_object_new_int
(le32_to_cpu(entry_count)))
;
2550
2551 ptr += 10;
2552
2553 timestamp = (le64_to_cpu(entry->power_on_hour) & 0x0000FFFFFFFFFFFFUL) / 1000;
2554 hours = timestamp / 3600;
2555 minutes = (timestamp % 3600) / 60;
2556 seconds = (timestamp % 3600) % 60;
2557 sprintf(ptr, "|%"PRIu64"l" "u"":%hhu:%hhu", (uint64_t)hours, minutes, seconds);
2558 if (is_json)
2559 json_object_add_value_string(stats, "Power On Hour", ptr+1);
2560
2561 ptr += 16;
2562
2563 sprintf(ptr, "| %"PRIu64"l" "u", le64_to_cpu(entry->power_cycle_count));
2564 if (is_json)
2565 json_object_add_value_int(stats, "Power cycle count",json_object_object_add(stats, "Power cycle count", json_object_new_int
(le32_to_cpu(entry->power_cycle_count)))
2566 le32_to_cpu(entry->power_cycle_count))json_object_object_add(stats, "Power cycle count", json_object_new_int
(le32_to_cpu(entry->power_cycle_count)))
;
2567
2568 ptr += 10;
2569
2570 /* firmware details */
2571 memset(buffer, 0, sizeof(buffer));
2572 memcpy(buffer, entry->previous_fw, sizeof(entry->previous_fw));
2573 sprintf(ptr, "| %s", buffer);
2574 if (is_json)
2575 json_object_add_value_string(stats, "Previous firmware", buffer);
2576
2577 ptr += 11;
2578
2579 memset(buffer, 0, sizeof(buffer));
2580 memcpy(buffer, entry->activated_fw, sizeof(entry->activated_fw));
2581 sprintf(ptr, "| %s", buffer);
2582 if (is_json)
2583 json_object_add_value_string(stats, "New FW activated", buffer);
2584
2585 ptr += 12;
2586
2587 /* firmware slot and commit action*/
2588 sprintf(ptr, "| %d", entry->slot);
2589 if (is_json)
2590 json_object_add_value_int(stats, "Slot number", entry->slot)json_object_object_add(stats, "Slot number", json_object_new_int
(entry->slot))
;
2591
2592 ptr += 9;
2593
2594 if (entry->commit_action_type <= 3)
2595 sprintf(ptr, "| %s", ca[entry->commit_action_type]);
2596 else
2597 sprintf(ptr, "| xxxb");
2598
2599 if (is_json)
2600 json_object_add_value_string(stats, "Commit Action Type", ptr+2);
2601
2602 ptr += 9;
2603
2604 /* result */
2605 if (entry->result)
2606 sprintf(ptr, "| Fail #%d", entry->result);
2607 else
2608 sprintf(ptr, "| pass");
2609
2610 if (is_json) {
2611 json_object_add_value_string(stats, "Result", ptr+2);
2612 return 0;
2613 }
2614
2615 /* replace all null characters with spaces */
2616 ptr = formatted_entry;
2617 while (index < entry_size) {
2618 if (ptr[index] == '\0')
2619 ptr[index] = ' ';
2620 index++;
2621 }
2622 return 0;
2623}
2624
2625static void micron_fw_activation_history_header_print(void)
2626{
2627 /* header to be printed field widths = 10 | 12 | 10 | 11 | 12 | 9 | 9 | 9 */
2628 printf("__________________________________________________________________________________\n");
2629 printf(" | | | | | | |\n");
2630 printf("Firmware | Power On | Power | Previous | New FW | Slot | Commit | Result\n");
2631 printf("Activation| Hour | cycle | firmware | activated | number | Action |\n");
2632 printf("Counter | | count | | | | Type |\n");
2633 printf("__________|___________|_________|__________|___________|________|________|________\n");
2634}
2635
2636static int micron_fw_activation_history(int argc, char **argv, struct command *acmd,
2637 struct plugin *plugin)
2638{
2639 const char *desc = "Retrieve Firmware Activation history of the given drive";
2640 char formatted_output[100];
2641 int count = 0;
2642 unsigned int logC2[C2_log_size4096/sizeof(int)] = { 0 };
2643 enum eDriveModel eModel = UNKNOWN_MODEL;
2644 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
2645 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
2646 int err;
2647 bool_Bool is_json = false0;
2648 struct json_object *root, *fw_act, *element;
2649 struct json_object *entry;
2650 struct format {
2651 char *fmt;
2652 };
2653
2654 const char *fmt = "output format normal|json";
2655 struct format cfg = {
2656 .fmt = "normal",
2657 };
2658
2659 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
2660 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
2661
2662 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
2663 if (err < 0)
2664 return -1;
2665
2666
2667 if (!strcmp(cfg.fmt, "json"))
2668 is_json = true1;
2669
2670 /* check if product supports fw_history log */
2671 err = -EINVAL22;
2672 if ((eModel != M51CX) && (eModel != M51BY) && (eModel != M51CY)
2673 && (eModel != M6003) && (eModel != M6004)) {
2674 fprintf(stderrstderr, "Unsupported drive model for vs-fw-activate-history command\n");
2675 goto out;
2676 }
2677
2678 err = nvme_get_log_simple(hdl, 0xC2, logC2, C2_log_size4096);
2679 if (err) {
2680 nvme_show_err(err, "Failed to retrieve fw activation history log");
2681 goto out;
2682 }
2683
2684 /* check if we have at least one entry to print */
2685 struct micron_fw_activation_history_table *table =
2686 (struct micron_fw_activation_history_table *)logC2;
2687
2688 /* check version and log page */
2689 if (table->log_page != 0xC2 || (table->version != 2 && table->version != 1)) {
2690 fprintf(stderrstderr, "Unsupported fw activation history page: %x, version: %x\n",
2691 table->log_page, table->version);
2692 goto out;
2693 }
2694
2695 if (!table->num_entries) {
2696 fprintf(stderrstderr, "No entries were found in fw activation history log\n");
2697 goto out;
2698 }
2699
2700 if (is_json) {
2701 root = json_create_object()json_object_new_object();
2702 fw_act = json_create_object()json_object_new_object();
2703 json_object_add_value_object(root, "vs-fw-activation-history", fw_act)json_object_object_add(root, "vs-fw-activation-history", fw_act
)
;
2704 json_object_add_value_int(fw_act, "Total Entry Num",json_object_object_add(fw_act, "Total Entry Num", json_object_new_int
(le32_to_cpu(table->num_entries)))
2705 le32_to_cpu(table->num_entries))json_object_object_add(fw_act, "Total Entry Num", json_object_new_int
(le32_to_cpu(table->num_entries)))
;
2706 entry = json_create_array()json_object_new_array();
2707 json_object_add_value_array(fw_act, "Entry", entry)json_object_object_add(fw_act, "Entry", entry);
2708 for (count = 0; count < table->num_entries; count++) {
2709 element = json_create_object()json_object_new_object();
2710 if (display_fw_activate_entry(count, &table->entries[count],
2711 formatted_output, element) == 0) {
2712 json_array_add_value_object(entry, element)json_object_array_add(entry, element);
2713 }
2714 }
2715 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
2716 printf("\n");
2717 json_free_object(root)json_object_put(root);
2718 } else {
2719 micron_fw_activation_history_header_print();
2720 for (count = 0; count < table->num_entries; count++) {
2721 memset(formatted_output, '\0', 100);
2722 if (!display_fw_activate_entry(count, &table->entries[count],
2723 formatted_output, NULL((void*)0)))
2724 printf("%s\n", formatted_output);
2725 }
2726 }
2727out:
2728 return err;
2729}
2730
2731#define MICRON_FID_LATENCY_MONITOR0xD0 0xD0
2732#define MICRON_LOG_LATENCY_MONITOR0xD1 0xD1
2733
2734static int micron_latency_stats_track(int argc, char **argv, struct command *acmd,
2735 struct plugin *plugin)
2736{
2737 int err = 0;
2738 __u64 result = 0;
2739 const char *desc = "Enable, Disable or Get cmd latency monitoring stats";
2740 const char *option = "enable or disable or status, default is status";
2741 const char *cmdstr =
2742 "commands to monitor for - all|read|write|trim, default is all i.e, enabled for all commands"
2743 ;
2744 const char *thrtime =
2745 "The threshold value to use for latency monitoring in milliseconds, default is 800ms"
2746 ;
2747
2748 int fid = MICRON_FID_LATENCY_MONITOR0xD0;
2749 enum eDriveModel model = UNKNOWN_MODEL;
2750 uint32_t command_mask = 0x7; /* 1:read 2:write 4:trim 7:all */
2751 uint32_t timing_mask = 0x08080800; /* R[31-24]:W[23:16]:T[15:8]:0 */
2752 uint32_t enable = 2;
2753 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
2754 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
2755 struct {
2756 char *option;
2757 char *command;
2758 uint32_t threshold;
2759 } opt = {
2760 .option = "status",
2761 .command = "all",
2762 .threshold = 0
2763 };
2764
2765 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"command", 'c', "command", CFG_STRING, &
opt.command, 1, cmdstr, 0, }, {"threshold", 't', "NUM", CFG_POSITIVE
, &opt.threshold, 1, thrtime, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
2766 OPT_STRING("option", 'O', "option", &opt.option, option),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"command", 'c', "command", CFG_STRING, &
opt.command, 1, cmdstr, 0, }, {"threshold", 't', "NUM", CFG_POSITIVE
, &opt.threshold, 1, thrtime, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
2767 OPT_STRING("command", 'c', "command", &opt.command, cmdstr),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"command", 'c', "command", CFG_STRING, &
opt.command, 1, cmdstr, 0, }, {"threshold", 't', "NUM", CFG_POSITIVE
, &opt.threshold, 1, thrtime, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
2768 OPT_UINT("threshold", 't', &opt.threshold, thrtime))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"command", 'c', "command", CFG_STRING, &
opt.command, 1, cmdstr, 0, }, {"threshold", 't', "NUM", CFG_POSITIVE
, &opt.threshold, 1, thrtime, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
;
2769
2770
2771 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
2772 if (err < 0)
2773 return -1;
2774
2775 if (!strcmp(opt.option, "enable")) {
2776 enable = 1;
2777 } else if (!strcmp(opt.option, "disable")) {
2778 enable = 0;
2779 } else if (strcmp(opt.option, "status")) {
2780 printf("Invalid control option %s specified\n", opt.option);
2781 return -1;
2782 }
2783
2784 err = nvme_get_features(hdl, 0, fid, 0, 0, 0, NULL((void*)0), 0, &result);
2785 if (err) {
2786 printf("Failed to retrieve latency monitoring feature status\n");
2787 return err;
2788 }
2789
2790 /* If it is to retrieve the status only */
2791 if (enable == 2) {
2792 printf("Latency Tracking Statistics is currently %s",
2793 (result & 0xFFFF0000) ? "enabled" : "disabled");
2794 if ((result & 7) == 7) {
2795 printf(" for All commands\n");
2796 } else if ((result & 7) > 0) {
2797 printf(" for");
2798 if (result & 1)
2799 printf(" Read");
2800 if (result & 2)
2801 printf(" Write");
2802 if (result & 4)
2803 printf(" Trim");
2804 printf(" commands\n");
2805 } else if (!result) {
2806 printf("\n");
2807 }
2808 return err;
2809 }
2810
2811 /* read and validate threshold values if enable option is specified */
2812 if (enable == 1) {
2813 if (opt.threshold > 2550) {
2814 printf("The maximum threshold value cannot be more than 2550 ms\n");
2815 return -1;
2816 } else if (opt.threshold % 10) {
2817 /* timing mask is in terms of 10ms units, so min allowed is 10ms */
2818 printf("The threshold value should be multiple of 10 ms\n");
2819 return -1;
2820 }
2821 opt.threshold /= 10;
2822 }
2823
2824 /* read-in command(s) to be monitored */
2825 if (!strcmp(opt.command, "read")) {
2826 command_mask = 0x1;
2827 timing_mask = (opt.threshold << 24);
2828 } else if (!strcmp(opt.command, "write")) {
2829 command_mask = 0x2;
2830 timing_mask = (opt.threshold << 16);
2831 } else if (!strcmp(opt.command, "trim")) {
2832 command_mask = 0x4;
2833 timing_mask = (opt.threshold << 8);
2834 } else if (strcmp(opt.command, "all")) {
2835 printf("Invalid command %s specified for option %s\n",
2836 opt.command, opt.option);
2837 return -1;
2838 }
2839
2840 err = nvme_set_features(hdl, 0, MICRON_FID_LATENCY_MONITOR0xD0, 1, enable,
2841 command_mask, timing_mask, 0, 0, NULL((void*)0), 0, &result);
2842 if (!err) {
2843 printf("Successfully %sd latency monitoring for %s commands with %dms threshold\n",
2844 opt.option, opt.command, !opt.threshold ? 800 : opt.threshold * 10);
2845 } else {
2846 printf("Failed to %s latency monitoring for %s commands with %dms threshold\n",
2847 opt.option, opt.command, !opt.threshold ? 800 : opt.threshold * 10);
2848 }
2849
2850 return err;
2851}
2852
2853
2854static int micron_latency_stats_logs(int argc, char **argv, struct command *acmd,
2855 struct plugin *plugin)
2856{
2857#define LATENCY_LOG_ENTRIES16 16
2858 struct latency_log_entry {
2859 uint64_t timestamp;
2860 uint32_t latency;
2861 uint32_t cmdtag;
2862 union {
2863 struct {
2864 uint32_t opcode:8;
2865 uint32_t fuse:2;
2866 uint32_t rsvd1:4;
2867 uint32_t psdt:2;
2868 uint32_t cid:16;
2869 };
2870 uint32_t dw0;
2871 };
2872 uint32_t nsid;
2873 uint32_t slba_low;
2874 uint32_t slba_high;
2875 union {
2876 struct {
2877 uint32_t nlb:16;
2878 uint32_t rsvd2:9;
2879 uint32_t deac:1;
2880 uint32_t prinfo:4;
2881 uint32_t fua:1;
2882 uint32_t lr:1;
2883 };
2884 uint32_t dw12;
2885 };
2886 uint32_t dsm;
2887 uint32_t rfu[6];
2888 } log[LATENCY_LOG_ENTRIES16];
2889 enum eDriveModel model = UNKNOWN_MODEL;
2890 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
2891 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
2892 int err = -1;
2893 const char *desc = "Display Latency tracking log information";
2894
2895 NVME_ARGS(opts)struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
2896
2897 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
2898 if (err)
2899 return err;
2900 memset(&log, 0, sizeof(log));
2901 err = nvme_get_log_simple(hdl, 0xD1, &log, sizeof(log));
2902 if (err) {
2903 nvme_show_err(err, "Unable to retrieve the latency stats log");
2904 return err;
2905 }
2906 /* print header and each log entry */
2907 printf("Timestamp, Latency, CmdTag, Opcode, Fuse, Psdt, Cid, Nsid, Slba_L, Slba_H, Nlb, ");
2908 printf("DEAC, PRINFO, FUA, LR\n");
2909 for (int i = 0; i < LATENCY_LOG_ENTRIES16; i++)
2910 printf("%"PRIu64"l" "u"",%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n",
2911 log[i].timestamp, log[i].latency, log[i].cmdtag, log[i].opcode,
2912 log[i].fuse, log[i].psdt, log[i].cid, log[i].nsid,
2913 log[i].slba_low, log[i].slba_high, log[i].nlb,
2914 log[i].deac, log[i].prinfo, log[i].fua, log[i].lr);
2915 printf("\n");
2916 return err;
2917}
2918
2919static int micron_latency_stats_info(int argc, char **argv, struct command *acmd,
2920 struct plugin *plugin)
2921{
2922 const char *desc = "display command latency statistics";
2923 const char *cmdstr = "command to display stats - all|read|write|trim, default is all";
2924 int err = 0;
2925 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
2926 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
2927 enum eDriveModel model = UNKNOWN_MODEL;
2928 #define LATENCY_BUCKET_COUNT32 32
2929 #define LATENCY_BUCKET_RSVD32 32
2930 struct micron_latency_stats {
2931 uint64_t version; /* major << 32 | minior */
2932 uint64_t all_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
2933 uint64_t read_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
2934 uint64_t write_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
2935 uint64_t trim_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
2936 uint32_t reserved[255]; /* round up to 4K */
2937 } log;
2938
2939 struct latency_thresholds {
2940 uint32_t start;
2941 uint32_t end;
2942 char *unit;
2943 } thresholds[LATENCY_BUCKET_COUNT32] = {
2944 {0, 50, "us"}, {50, 100, "us"}, {100, 150, "us"}, {150, 200, "us"},
2945 {200, 300, "us"}, {300, 400, "us"}, {400, 500, "us"}, {500, 600, "us"},
2946 {600, 700, "us"}, {700, 800, "us"}, {800, 900, "us"}, {900, 1000, "us"},
2947 {1, 5, "ms"}, {5, 10, "ms"}, {10, 20, "ms"}, {20, 50, "ms"}, {50, 100, "ms"},
2948 {100, 200, "ms"}, {200, 300, "ms"}, {300, 400, "ms"}, {400, 500, "ms"},
2949 {500, 600, "ms"}, {600, 700, "ms"}, {700, 800, "ms"}, {800, 900, "ms"},
2950 {900, 1000, "ms"}, {1, 2, "s"}, {2, 3, "s"}, {3, 4, "s"}, {4, 5, "s"},
2951 {5, 8, "s"},
2952 {8, INT_MAX2147483647, "s"},
2953 };
2954
2955 struct {
2956 char *command;
2957 } opt = {
2958 .command = "all"
2959 };
2960
2961 uint64_t *cmd_stats = &log.all_cmds[0];
2962 char *cmd_str = "All";
2963
2964 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"command", 'c', "command", CFG_STRING, &opt.command
, 1, cmdstr, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, (
(void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose", 'v'
, "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
2965 OPT_STRING("command", 'c', "command", &opt.command, cmdstr))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"command", 'c', "command", CFG_STRING, &opt.command
, 1, cmdstr, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, (
(void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose", 'v'
, "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
;
2966
2967 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
2968 if (err < 0)
2969 return err;
2970 if (!strcmp(opt.command, "read")) {
2971 cmd_stats = &log.read_cmds[0];
2972 cmd_str = "Read";
2973 } else if (!strcmp(opt.command, "write")) {
2974 cmd_stats = &log.write_cmds[0];
2975 cmd_str = "Write";
2976 } else if (!strcmp(opt.command, "trim")) {
2977 cmd_stats = &log.trim_cmds[0];
2978 cmd_str = "Trim";
2979 } else if (strcmp(opt.command, "all")) {
2980 printf("Invalid command option %s to display latency stats\n", opt.command);
2981 return -1;
2982 }
2983
2984 memset(&log, 0, sizeof(log));
2985 err = nvme_get_log_simple(hdl, 0xD0, &log, sizeof(log));
2986 if (err) {
2987 nvme_show_err(err, "Unable to retrieve latency stats log for the drive");
2988 return err;
2989 }
2990 printf("Micron IO %s Command Latency Statistics\n"
2991 "Major Revision : %d\nMinor Revision : %d\n",
2992 cmd_str, (int)(log.version >> 32), (int)(log.version & 0xFFFFFFFF));
2993 printf("=============================================\n");
2994 printf("Bucket Start End Command Count\n");
2995 printf("=============================================\n");
2996
2997 for (int b = 0; b < LATENCY_BUCKET_COUNT32; b++) {
2998 int bucket = b + 1;
2999 char start[32] = { 0 };
3000 char end[32] = { 0 };
3001
3002 sprintf(start, "%u%s", thresholds[b].start, thresholds[b].unit);
3003 if (thresholds[b].end == INT_MAX2147483647)
3004 sprintf(end, "INF");
3005 else
3006 sprintf(end, "%u%s", thresholds[b].end, thresholds[b].unit);
3007 printf("%2d %8s %8s %8"PRIu64"l" "u""\n", bucket, start, end, cmd_stats[b]);
3008 }
3009 return err;
3010}
3011
3012static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *acmd,
3013 struct plugin *plugin)
3014{
3015 const char *desc = "Retrieve Smart or Extended Smart Health log for the given device ";
3016 unsigned int logC0[C0_log_size512/sizeof(int)] = { 0 };
3017 unsigned int logFB[FB_log_size512/sizeof(int)] = { 0 };
3018 struct nvme_id_ctrl ctrl;
3019 enum eDriveModel eModel = UNKNOWN_MODEL;
3020 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3021 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3022 bool_Bool is_json = true1;
3023 nsze_from_oacs = false0;
3024 struct format {
3025 char *fmt;
3026 };
3027 const char *fmt = "output format normal|json";
3028 struct format cfg = {
3029 .fmt = "json",
3030 };
3031 int err = 0;
3032
3033 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
3034 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
3035
3036 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
3037 if (err < 0)
3038 return -1;
3039
3040 if (!strcmp(cfg.fmt, "normal"))
3041 is_json = false0;
3042
3043 /* For M5410 and M5407, this option prints 0xFB log page */
3044 if (eModel == M5410 || eModel == M5407) {
3045 __u8 spec = (eModel == M5410) ? 0 : 1;
3046 __u8 nsze;
3047
3048 err = nvme_identify_ctrl(hdl, &ctrl);
3049 if (!err)
3050 err = nvme_get_log_simple(hdl, 0xFB, logFB, FB_log_size512);
3051 if (err) {
3052 nvme_show_err(err, "Unable to retrieve smart log 0xFB for the drive");
3053 goto out;
3054 }
3055
3056 nsze = (ctrl.vs[987] == 0x12);
3057 if (!nsze && nsze_from_oacs)
3058 nsze = ((ctrl.oacs >> 3) & 0x1);
3059 print_nand_stats_fb((__u8 *)logFB, NULL((void*)0), nsze, is_json, spec);
3060 goto out;
3061 }
3062
3063 /* check for models that support 0xC0 log */
3064 if ((eModel != M51CX) && (eModel != M51BY) && (eModel != M51CY)
3065 && (eModel != M6003) && (eModel != M6004)) {
3066 printf("Unsupported drive model for vs-smart-add-log command\n");
3067 err = -1;
3068 goto out;
3069 }
3070
3071 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
3072 if (!err)
3073 print_smart_cloud_health_log((__u8 *)logC0, is_json, eModel);
3074 else
3075 nvme_show_err(err, "Unable to retrieve extended smart log 0xC0 for the drive");
3076out:
3077 return err;
3078}
3079
3080static int micron_clr_fw_activation_history(int argc, char **argv,
3081 struct command *command, struct plugin *plugin)
3082{
3083 const char *desc = "Clear FW activation history";
3084 __u64 result = 0;
3085 __u8 fid = MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY0xC1;
3086 enum eDriveModel model = UNKNOWN_MODEL;
3087 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3088 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3089
3090 NVME_ARGS(opts)struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
3091 int err = 0;
3092
3093 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3094 if (err < 0)
3095 return err;
3096
3097 if ((model != M51CX) && (model != M51BY) && (model != M51CY)
3098 && (model != M6003) && (model != M6004)) {
3099 printf("This option is not supported for specified drive\n");
3100 return err;
3101 }
3102
3103 err = nvme_set_features_simple(hdl, 1 << 31, fid, 0, 0, &result);
3104 if (!err)
3105 err = (int)result;
3106 else
3107 nvme_show_err(err, "Failed to clear fw activation history");
3108
3109 return err;
3110}
3111
3112static int micron_telemetry_cntrl_option(int argc, char **argv,
3113 struct command *command, struct plugin *plugin)
3114{
3115 int err = 0;
3116 __u64 result = 0;
3117 const char *desc = "Enable or Disable Controller telemetry log generation";
3118 const char *option = "enable or disable or status";
3119 const char *select =
3120 "select/save values: enable/disable options1 - save (persistent), 0 - non-persistent and for status options: 0 - current, 1 - default, 2-saved"
3121 ;
3122
3123 int fid = MICRON_FEATURE_TELEMETRY_CONTROL_OPTION0xCF;
3124 enum eDriveModel model = UNKNOWN_MODEL;
3125 struct nvme_id_ctrl ctrl = { 0 };
3126 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3127 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3128
3129 struct {
3130 char *option;
3131 int select;
3132 } opt = {
3133 .option = "disable",
3134 .select = 0,
3135 };
3136
3137 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"select", 's', "NUM", CFG_POSITIVE, &opt
.select, 1, select, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
3138 OPT_STRING("option", 'O', "option", &opt.option, option),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"select", 's', "NUM", CFG_POSITIVE, &opt
.select, 1, select, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
3139 OPT_UINT("select", 's', &opt.select, select))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"option", 'O', "option", CFG_STRING, &opt.option,
1, option, 0, }, {"select", 's', "NUM", CFG_POSITIVE, &opt
.select, 1, select, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
;
3140
3141 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3142 if (err < 0)
3143 return -1;
3144
3145 err = nvme_identify_ctrl(hdl, &ctrl);
3146 if ((ctrl.lpa & 0x8) != 0x8) {
3147 printf("drive doesn't support host/controller generated telemetry logs\n");
3148 return err;
3149 }
3150
3151 if (!strcmp(opt.option, "enable")) {
3152 err = nvme_set_features(hdl, 1, fid, (opt.select & 0x1), 1, 0, 0, 0, 0,
3153 NULL((void*)0), 0, &result);
3154 if (!err)
3155 printf("successfully set controller telemetry option\n");
3156 else
3157 printf("Failed to set controller telemetry option\n");
3158 } else if (!strcmp(opt.option, "disable")) {
3159 err = nvme_set_features(hdl, 1, fid, (opt.select & 0x1), 0, 0, 0, 0, 0,
3160 NULL((void*)0), 0, &result);
3161 if (!err)
3162 printf("successfully disabled controller telemetry option\n");
3163 else
3164 printf("Failed to disable controller telemetry option\n");
3165 } else if (!strcmp(opt.option, "status")) {
3166 err = nvme_get_features(hdl, 1, fid, opt.select & 0x3, 0, 0, NULL((void*)0), 0,
3167 &result);
3168 if (!err)
3169 printf("Controller telemetry option : %s\n",
3170 (result) ? "enabled" : "disabled");
3171 else
3172 printf("Failed to retrieve controller telemetry option\n");
3173 } else {
3174 printf("invalid option %s, valid values are enable,disable or status\n",
3175 opt.option);
3176 return -1;
3177 }
3178
3179 return err;
3180}
3181
3182/* M51XX models log page header */
3183struct micron_common_log_header {
3184 uint8_t id;
3185 uint8_t version;
3186 uint16_t pn;
3187 uint32_t log_size;
3188 uint32_t max_size;
3189 uint32_t write_pointer;
3190 uint32_t next_pointer;
3191 uint32_t overwritten_bytes;
3192 uint8_t flags;
3193 uint8_t reserved[7];
3194};
3195
3196/* helper function to retrieve logs with specific offset and max chunk size */
3197int nvme_get_log_lpo(struct libnvme_transport_handle *hdl, __u8 log_id, __u32 lpo, __u32 chunk,
3198 __u32 data_len, void *data)
3199{
3200 __u32 offset = lpo, xfer_len = data_len;
3201 struct libnvme_passthru_cmd cmd;
3202 void *ptr = data;
3203 int ret = 0;
3204
3205 /* divide data into multiple chunks */
3206 do {
3207 xfer_len = data_len - offset;
3208 if (xfer_len > chunk)
3209 xfer_len = chunk;
3210
3211 nvme_init_get_log(&cmd, NVME_NSID_ALL, log_id, NVME_CSI_NVM,
3212 ptr, xfer_len);
3213 nvme_init_get_log_lpo(&cmd, lpo);
3214 ret = libnvme_get_log(hdl, &cmd, false0, xfer_len);
3215 if (ret)
3216 return ret;
3217 offset += xfer_len;
3218 ptr += xfer_len;
3219 } while (offset < data_len);
3220 return ret;
3221}
3222
3223/* retrieves logs with common log format */
3224static int get_common_log(struct libnvme_transport_handle *hdl, uint8_t id, uint8_t **buf, int *size)
3225{
3226 struct micron_common_log_header hdr = { 0 };
3227 int log_size = sizeof(hdr), first = 0, second = 0;
3228 uint8_t *buffer = NULL((void*)0);
3229 int ret = -1;
3230 int chunk = 0x4000; /* max chunk size to be used for these logs */
3231
3232 ret = nvme_get_log_simple(hdl, id, &hdr, sizeof(hdr));
3233 if (ret) {
3234 fprintf(stderrstderr, "pull hdr failed for %u with error: 0x%x\n", id, ret);
3235 return ret;
3236 }
3237
3238 if (hdr.id != id || !hdr.log_size || !hdr.max_size ||
3239 hdr.write_pointer < sizeof(hdr)) {
3240 fprintf(stderrstderr,
3241 "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, max: %u, wp: %u, flags: %u, np: %u\n"
3242 , id, hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, hdr.flags,
3243 hdr.next_pointer);
3244 return 1;
3245 }
3246
3247 /*
3248 * we may have just 32-bytes for some models; write to wfile if log hasn't
3249 * yet reached its max size
3250 */
3251 if (hdr.log_size == sizeof(hdr)) {
3252 buffer = (uint8_t *)libnvme_alloc(sizeof(hdr));
3253 if (!buffer) {
3254 fprintf(stderrstderr, "malloc of %zu bytes failed for log: 0x%X\n",
3255 sizeof(hdr), id);
3256 return -ENOMEM12;
3257 }
3258 memcpy(buffer, (uint8_t *)&hdr, sizeof(hdr));
3259 } else if (hdr.log_size < hdr.max_size) {
3260 buffer = (uint8_t *)libnvme_alloc(sizeof(hdr) + hdr.log_size);
3261 if (!buffer) {
3262 fprintf(stderrstderr, "malloc of %zu bytes failed for log: 0x%X\n",
3263 hdr.log_size + sizeof(hdr), id);
3264 return -ENOMEM12;
3265 }
3266 memcpy(buffer, &hdr, sizeof(hdr));
3267 ret = nvme_get_log_lpo(hdl, id, sizeof(hdr), chunk, hdr.log_size,
3268 buffer + sizeof(hdr));
3269 if (!ret)
3270 log_size += hdr.log_size;
3271 } else if (hdr.log_size >= hdr.max_size) {
3272 /*
3273 * reached maximum, to maintain, sequence we need to depend on write
3274 * pointer to detect wrap-overs. FW doesn't yet implement the condition
3275 * hdr.log_size > hdr.max_size; also ignore over-written log data; we
3276 * also ignore collisions for now
3277 */
3278 buffer = (uint8_t *)libnvme_alloc(hdr.max_size + sizeof(hdr));
3279 if (!buffer) {
3280 fprintf(stderrstderr, "malloc of %zu bytes failed for log: 0x%X\n",
3281 hdr.max_size + sizeof(hdr), id);
3282 return -ENOMEM12;
3283 }
3284 memcpy(buffer, &hdr, sizeof(hdr));
3285
3286 first = hdr.max_size - hdr.write_pointer;
3287 second = hdr.write_pointer - sizeof(hdr);
3288
3289 if (first) {
3290 ret = nvme_get_log_lpo(hdl, id, hdr.write_pointer, chunk, first,
3291 buffer + sizeof(hdr));
3292 if (ret) {
3293 libnvme_free(buffer);
3294 fprintf(stderrstderr, "failed to get log: 0x%X\n", id);
3295 return ret;
3296 }
3297 log_size += first;
3298 }
3299 if (second) {
3300 ret = nvme_get_log_lpo(hdl, id, sizeof(hdr), chunk, second,
3301 buffer + sizeof(hdr) + first);
3302 if (ret) {
3303 fprintf(stderrstderr, "failed to get log: 0x%X\n", id);
3304 libnvme_free(buffer);
3305 return ret;
3306 }
3307 log_size += second;
3308 }
3309 }
3310 *buf = buffer;
3311 *size = log_size;
3312 return ret;
3313}
3314
3315static int GetOcpEnhancedTelemetryLog(struct libnvme_transport_handle *hdl, const char *dir, int nLogID)
3316{
3317 int err = 0;
3318 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned char *pTelemetryDataHeader = NULL((void*)0);
3319 unsigned int nallocSize = 0;
3320 unsigned int nOffset = 0;
3321 unsigned char *pTelemetryBuffer = NULL((void*)0);
3322 unsigned int usAreaLastBlock[4] = {0};
3323 bool_Bool bTeleheaderWrite = true1;
3324 /* Enable ETDAS */
3325 unsigned int uiBufferSize = 512;
3326 unsigned char pBuffer[512] = { 0 };
3327 __u64 result = 0;
3328
3329 pBuffer[1] = 1;
3330
3331 err = nvme_set_features(hdl, NVME_NSID_ALL,
3332 MICRON_FEATURE_OCP_ENHANCED_TELEMETRY0x16, 1, 0, 0, 0,
3333 0, 0, pBuffer, uiBufferSize, &result);
3334
3335 if (err != 0)
3336 printf("Failed to set ETDAS, Data Area 4 won't be avialable >>> ");
3337
3338 /* Read Telemetry header information */
3339 pTelemetryDataHeader = (unsigned char *)libnvme_alloc(512);
3340
3341 if (!pTelemetryDataHeader) {
3342 printf("Unable to allocate buffer of size 0x%X bytes for telemetry header", 512);
3343 return -1;
3344 }
3345 err = NVMEGetLogPage(hdl, nLogID, pTelemetryDataHeader, 512, 0);
3346
3347 if (err != 0)
3348 return err;
3349
3350 nOffset += 512;
3351 int n = 8;
3352 /* Get size of log page */
3353 for (int i = 0; i < 3; i++) {
3354 usAreaLastBlock[i] = (pTelemetryDataHeader[n + 1] << 8) | pTelemetryDataHeader[n];
3355 n += 2;
3356 }
3357 n += 2;
3358 usAreaLastBlock[3] = (pTelemetryDataHeader[n + 3] << 24) |
3359 (pTelemetryDataHeader[n + 2] << 16) |
3360 (pTelemetryDataHeader[n + 1] << 8) |
3361 pTelemetryDataHeader[n];
3362
3363 for (int nArea = 0; nArea <= 3; nArea++) {
3364 if (nArea != 0)
3365 nallocSize = (usAreaLastBlock[nArea] - usAreaLastBlock[nArea - 1]) * 512;
3366 else
3367 nallocSize = usAreaLastBlock[nArea] * 512;
3368
3369 if (nallocSize == 0) {
3370 printf(
3371 "Enhanced Telemetry log Data Area %d Size is zero, continuing with next available Data Area\n"
3372 , (nArea + 1));
3373 continue;
3374 }
3375
3376 pTelemetryBuffer = (unsigned char *)libnvme_alloc(nallocSize);
3377 if (!pTelemetryBuffer) {
3378 printf(
3379 "Unable to allocate buffer of size 0x%X bytes for Data Area %d"
3380 , nallocSize, (nArea + 1)
3381 );
3382 nOffset += nallocSize;
3383 continue;
3384 }
3385 /* Fetch the Data */
3386 err = NVMEGetLogPage(hdl, nLogID, pTelemetryBuffer, nallocSize, nOffset);
3387
3388 if (err != 0) {
3389 printf(
3390 "Failed to fetch telemetry data of size : %u from offset : %u!\n"
3391 , nallocSize, nOffset
3392 );
3393 libnvme_free(pTelemetryBuffer);
3394 pTelemetryBuffer = NULL((void*)0);
3395 nOffset += nallocSize;
3396 continue;
3397 }
3398
3399 /* Increment the Offset value */
3400 nOffset += nallocSize;
3401
3402 if ((nArea + 1) <= 4) {
3403 char strBuffer[256] = { 0 };
3404
3405 if (nLogID == NVME_LOG_LID_TELEMETRY_HOST) {
3406 sprintf(strBuffer, "%s", "nvme_host_telemetry_log.bin");
3407 if (bTeleheaderWrite) {
3408 WriteData(pTelemetryDataHeader, 512, dir,
3409 "nvme_host_telemetry_log.bin", strBuffer);
3410 bTeleheaderWrite = false0;
3411 }
3412 WriteData(pTelemetryBuffer, nallocSize, dir,
3413 "nvme_host_telemetry_log.bin", strBuffer);
3414 } else if (nLogID == NVME_LOG_LID_TELEMETRY_CTRL) {
3415 sprintf(strBuffer, "%s", "nvme_controller_telemetry_log.bin");
3416 if (bTeleheaderWrite) {
3417 WriteData(pTelemetryDataHeader, 512, dir,
3418 "nvme_controller_telemetry_log.bin", strBuffer);
3419 bTeleheaderWrite = false0;
3420 }
3421 WriteData(pTelemetryBuffer, nallocSize, dir,
3422 "nvme_controller_telemetry_log.bin", strBuffer);
3423 }
3424 }
3425
3426 libnvme_free(pTelemetryBuffer);
3427 pTelemetryBuffer = NULL((void*)0);
3428 }
3429
3430 return err;
3431}
3432
3433
3434static int micron_internal_logs(int argc, char **argv, struct command *acmd,
3435 struct plugin *plugin)
3436{
3437 int err = -EINVAL22;
3438 int ctrlIdx, telemetry_option = 0;
3439 char strOSDirName[1024];
3440 char strCtrlDirName[1024];
3441 char strMainDirName[256];
3442 unsigned int *puiIDDBuf;
3443 unsigned int uiMask;
3444 struct nvme_id_ctrl ctrl;
3445 char sn[20] = { 0 };
3446 char msg[256] = { 0 };
3447 int c_logs_index = 8; /* should be current size of aVendorLogs */
3448 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3449 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3450 struct {
3451 unsigned char ucLogPage;
3452 const char *strFileName;
3453 int nLogSize;
3454 int nMaxSize;
3455 } aVendorLogs[32] = {
3456 { 0x03, "firmware_slot_info_log.bin", 512, 0 },
3457 { 0xC1, "nvmelog_C1.bin", 0, 0 },
3458 { 0xC2, "nvmelog_C2.bin", 0, 0 },
3459 { 0xC4, "nvmelog_C4.bin", 0, 0 },
3460 { 0xC5, "nvmelog_C5.bin", C5_log_size(((452 + 16 * 1024) / 4) * 4096), 0 },
3461 { 0xD0, "nvmelog_D0.bin", D0_log_size512, 0 },
3462 { 0xE6, "nvmelog_E6.bin", 0, 0 },
3463 { 0xE7, "nvmelog_E7.bin", 0, 0 }
3464 },
3465 aM51XXLogs[] = {
3466 { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */
3467 { 0xD0, "nvmelog_D0.bin", 512, 0 },
3468 { 0x03, "firmware_slot_info_log.bin", 512, 0},
3469 { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 },
3470 { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 },
3471 { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 },
3472 { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 },
3473 { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 }
3474 },
3475 aM51AXLogs[] = {
3476 { 0xCA, "nvmelog_CA.bin", 512, 0 },
3477 { 0xFA, "nvmelog_FA.bin", 4096, 15232 },
3478 { 0xF6, "nvmelog_F6.bin", 4096, 512 * 1024 },
3479 { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 },
3480 { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 },
3481 { 0x04, "changed_namespace_log.bin", 4096, 0 },
3482 { 0x05, "command_effects_log.bin", 4096, 0 },
3483 { 0x06, "drive_self_test.bin", 4096, 0 }
3484 },
3485 aM51BXLogs[] = {
3486 { 0xFA, "nvmelog_FA.bin", 4096, 16376 },
3487 { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 },
3488 { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 },
3489 { 0xCA, "nvmelog_CA.bin", 512, 1024 }
3490 },
3491 aM51CXLogs[] = {
3492 { 0xE1, "nvmelog_E1.bin", 0, 0 },
3493 { 0xE2, "nvmelog_E2.bin", 0, 0 },
3494 { 0xE3, "nvmelog_E3.bin", 0, 0 },
3495 { 0xE4, "nvmelog_E4.bin", 0, 0 },
3496 { 0xE5, "nvmelog_E5.bin", 0, 0 },
3497 { 0xE8, "nvmelog_E8.bin", 0, 0 },
3498 { 0xE9, "nvmelog_E9.bin", 0, 0 },
3499 { 0xEA, "nvmelog_EA.bin", 0, 0 }
3500 };
3501
3502 enum eDriveModel eModel;
3503
3504 const char *desc = "This retrieves the micron debug log package";
3505 const char *package = "Log output data file name (required)";
3506 const char *type = "telemetry log type - host or controller";
3507 const char *data_area = "telemetry log data area 1, 2, 3 or 4";
3508 unsigned char *dataBuffer = NULL((void*)0);
3509 int bSize = 0;
3510 int maxSize = 0;
3511 struct MICRON_WORKLOAD_LOG_HDR stWllHdr = { 0 };
3512
3513 struct config {
3514 char *type;
3515 char *package;
3516 int data_area;
3517 int log;
3518 };
3519
3520 struct config cfg = {
3521 .type = "",
3522 .package = "",
3523 .data_area = -1,
3524 .log = 0x07,
3525 };
3526
3527 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"type", 't', "log type", CFG_STRING, &cfg.type, 1
, type, 0, }, {"package", 'p', "FILE", CFG_STRING, &cfg.package
, 1, package, 0, }, {"data_area", 'd', "NUM", CFG_POSITIVE, &
cfg.data_area, 1, data_area, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
3528 OPT_STRING("type", 't', "log type", &cfg.type, type),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"type", 't', "log type", CFG_STRING, &cfg.type, 1
, type, 0, }, {"package", 'p', "FILE", CFG_STRING, &cfg.package
, 1, package, 0, }, {"data_area", 'd', "NUM", CFG_POSITIVE, &
cfg.data_area, 1, data_area, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
3529 OPT_STRING("package", 'p', "FILE", &cfg.package, package),struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"type", 't', "log type", CFG_STRING, &cfg.type, 1
, type, 0, }, {"package", 'p', "FILE", CFG_STRING, &cfg.package
, 1, package, 0, }, {"data_area", 'd', "NUM", CFG_POSITIVE, &
cfg.data_area, 1, data_area, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
3530 OPT_UINT("data_area", 'd', &cfg.data_area, data_area))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"type", 't', "log type", CFG_STRING, &cfg.type, 1
, type, 0, }, {"package", 'p', "FILE", CFG_STRING, &cfg.package
, 1, package, 0, }, {"data_area", 'd', "NUM", CFG_POSITIVE, &
cfg.data_area, 1, data_area, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR
, ((void*)0), 0, "Global options", 0, ((void*)0)}, {"verbose"
, 'v', "NUM", CFG_INCREMENT, &nvme_args.verbose, 0, "Increase output verbosity"
, 0, }, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args
.output_format, 1, "Output format: normal|json|binary|tabular"
, 0, }, {"timeout", 0, "NUM", CFG_POSITIVE, &nvme_args.timeout
, 1, "timeout value, in milliseconds", 0, }, {"dry-run", 0, (
(void*)0), CFG_FLAG, &nvme_args.dry_run, 0, "show command instead of executing"
, 0, }, {"no-retries", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_retries, 0, "disable retry logic on errors", 0, }, {"no-ioctl-probing"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_ioctl_probing, 0
, "disable 64-bit IOCTL support probing", 0, }, {"output-format-version"
, 0, "NUM", CFG_POSITIVE, &nvme_args.output_format_ver, 1
, "output format version: 1|2", 0, }, {"human-readable", 'H',
((void*)0), CFG_FLAG, &nvme_args.verbose, 0, ((void*)0),
0, ((void*)0), 1}, { ((void*)0) } }
;
3531
3532 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
3533 if (err)
3534 return err;
3535
3536 /* if telemetry type is specified, check for data area */
3537 if (strlen(cfg.type)) {
3538 if (!strcmp(cfg.type, "controller")) {
3539 cfg.log = 0x08;
3540 } else if (strcmp(cfg.type, "host")) {
3541 printf("telemetry type (host or controller) should be specified i.e. -t=host\n");
3542 goto out;
3543 }
3544
3545 if (cfg.data_area <= 0 || cfg.data_area > 4) {
3546 printf("data area must be selected using -d option ie --d=1,2,3,4\n");
3547 goto out;
3548 }
3549 telemetry_option = 1;
3550 } else if (cfg.data_area > 0) {
3551 printf(
3552 "data area option is valid only for telemetry option (i.e --type=host|controller)\n"
3553 );
3554 goto out;
3555 }
3556
3557 if (!strlen(cfg.package)) {
3558 if (telemetry_option)
3559 printf("Log data file must be specified. ie -p=logfile.bin\n");
3560 else
3561 printf(
3562 "Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz\n"
3563 );
3564 goto out;
3565 }
3566
3567 /* pull log details based on the model name */
3568 eModel = GetDriveModel(ctx, hdl);
3569 if (eModel == UNKNOWN_MODEL) {
3570 printf("Unsupported drive model for vs-internal-log collection\n");
3571 goto out;
3572 }
3573
3574 if (sscanf(libnvme_transport_handle_get_name(hdl), "nvme%d", &ctrlIdx) != 1)
3575 ctrlIdx = 0;
3576
3577 err = nvme_identify_ctrl(hdl, &ctrl);
3578 if (err)
3579 goto out;
3580
3581 err = -EINVAL22;
3582 if (telemetry_option) {
3583 if ((ctrl.lpa & 0x8) != 0x8) {
3584 printf("telemetry option is not supported for specified drive\n");
3585 goto out;
3586 }
3587 int logSize = 0; __u8 *buffer = NULL((void*)0); const char *dir = ".";
3588
3589 err = micron_telemetry_log(hdl, cfg.log, &buffer, &logSize,
3590 cfg.data_area);
3591 if (!err && logSize > 0 && buffer) {
3592 sprintf(msg, "telemetry log: 0x%X", cfg.log);
3593 WriteData(buffer, logSize, dir, cfg.package, msg);
3594 libnvme_free(buffer);
3595 }
3596 goto out;
3597 }
3598
3599 printf("Preparing log package. This will take a few seconds...\n");
3600
3601 /* trim spaces out of serial number string */
3602 int i, j = 0;
3603
3604 for (i = 0; i < sizeof(ctrl.sn); i++) {
3605 if (isblank((int)ctrl.sn[i])((*__ctype_b_loc ())[(int) (((int)ctrl.sn[i]))] & (unsigned
short int) _ISblank)
)
3606 continue;
3607 sn[j++] = ctrl.sn[i];
3608 }
3609 sn[j] = '\0';
3610 strcpy(ctrl.sn, sn);
3611
3612 SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName,
3613 strCtrlDirName);
3614
3615 GetTimestampInfo(strOSDirName);
3616 GetCtrlIDDInfo(strCtrlDirName, &ctrl);
3617 GetOSConfig(strOSDirName);
3618 GetDriveInfo(strOSDirName, ctrlIdx, &ctrl);
3619
3620 for (int i = 1; i <= ctrl.nn; i++)
3621 GetNSIDDInfo(hdl, strCtrlDirName, i);
3622
3623 GetSmartlogData(hdl, strCtrlDirName);
3624 GetErrorlogData(hdl, ctrl.elpe, strCtrlDirName);
3625 GetGenericLogs(hdl, strCtrlDirName);
3626 /* pull if telemetry log data is supported */
3627 if ((ctrl.lpa & 0x8) == 0x8) {
3628 if (eModel == M51BY) {
3629 err = GetOcpEnhancedTelemetryLog(hdl, strCtrlDirName,
3630 NVME_LOG_LID_TELEMETRY_HOST);
3631 if (err != 0)
3632 printf("Failed to fetch the host telemetry log");
3633
3634 err = GetOcpEnhancedTelemetryLog(hdl, strCtrlDirName,
3635 NVME_LOG_LID_TELEMETRY_CTRL);
3636 if (err != 0)
3637 printf("Failed to fetch the controller telemetry log");
3638 } else {
3639 GetTelemetryData(hdl, strCtrlDirName);
3640 }
3641 }
3642 GetFeatureSettings(hdl, strCtrlDirName);
3643
3644 if (eModel != M5410 && eModel != M5407) {
3645 memcpy(&aVendorLogs[c_logs_index], aM51XXLogs, sizeof(aM51XXLogs));
3646 c_logs_index += ARRAY_SIZE(aM51XXLogs)(sizeof(aM51XXLogs) / sizeof((aM51XXLogs)[0]));
3647 if (eModel == M51AX)
3648 memcpy((char *)&aVendorLogs[c_logs_index], aM51AXLogs, sizeof(aM51AXLogs));
3649 else if (eModel == M51BX)
3650 memcpy((char *)&aVendorLogs[c_logs_index], aM51BXLogs, sizeof(aM51BXLogs));
3651 else if (eModel == M51CX || eModel == M51BY || eModel == M51CY)
3652 memcpy((char *)&aVendorLogs[c_logs_index], aM51CXLogs, sizeof(aM51CXLogs));
3653 }
3654
3655 for (int i = 0; i < (int)(ARRAY_SIZE(aVendorLogs)(sizeof(aVendorLogs) / sizeof((aVendorLogs)[0]))) && aVendorLogs[i].ucLogPage; i++) {
3656 err = -1;
3657 switch (aVendorLogs[i].ucLogPage) {
3658 case 0xE1:
3659 case 0xE5:
3660 err = 1;
3661 break;
3662 case 0xE9:
3663 if (eModel == M51CX || eModel == M51BY) {
3664 err = NVMEGetLogPage(hdl, aVendorLogs[i].ucLogPage,
3665 (unsigned char *)&stWllHdr,
3666 sizeof(struct MICRON_WORKLOAD_LOG_HDR), 0);
3667 if (err == 0) {
3668 bSize = stWllHdr.uiLength;
3669 if (bSize > 0) {
3670 dataBuffer = (unsigned char *)libnvme_alloc(bSize);
3671 if (!dataBuffer) {
3672 printf(
3673 " Memory allocation failed for log id : 0x%02X\n"
3674 , aVendorLogs[i].ucLogPage);
3675 continue;
3676 }
3677 }
3678 memcpy(dataBuffer, &stWllHdr,
3679 sizeof(struct MICRON_WORKLOAD_LOG_HDR));
3680 err = NVMEGetLogPage(hdl,
3681 aVendorLogs[i].ucLogPage,
3682 (dataBuffer +
3683 sizeof(struct MICRON_WORKLOAD_LOG_HDR)),
3684 (bSize -
3685 sizeof(struct MICRON_WORKLOAD_LOG_HDR)),
3686 sizeof(struct MICRON_WORKLOAD_LOG_HDR));
3687 if (err != 0)
3688 printf("Failed to fetch the E9 logs\n");
3689
3690 }
3691 } else {
3692 err = 1;
3693 }
3694 break;
3695 case 0xE2:
3696 if (eModel == M51CX || eModel == M51BY || eModel == M51CY)
3697 continue;
3698
3699 err = get_common_log(hdl, aVendorLogs[i].ucLogPage,
3700 &dataBuffer, &bSize);
3701 break;
3702 case 0xE3:
3703 case 0xE4:
3704 case 0xE8:
3705 case 0xEA:
3706 err = get_common_log(hdl, aVendorLogs[i].ucLogPage,
3707 &dataBuffer, &bSize);
3708 break;
3709 case 0xC1:
3710 case 0xC2:
3711 if (eModel == M51CX || eModel == M51BY || eModel == M51CY)
3712 continue;
3713
3714 err = GetLogPageSize(hdl, aVendorLogs[i].ucLogPage, &bSize);
3715 if (err == 0 && bSize > 0)
3716 err = GetCommonLogPage(hdl,
3717 aVendorLogs[i].ucLogPage,
3718 &dataBuffer, bSize);
3719 break;
3720 case 0xC4:
3721 if (eModel == M51BY || eModel == M51CY)
3722 continue;
3723
3724 err = GetLogPageSize(hdl, aVendorLogs[i].ucLogPage,
3725 &bSize);
3726 if (!err && bSize > 0)
3727 err = GetCommonLogPage(hdl, aVendorLogs[i].ucLogPage,
3728 &dataBuffer, bSize);
3729 break;
3730 case 0xE6:
3731 case 0xE7:
3732 puiIDDBuf = (unsigned int *)&ctrl;
3733 uiMask = puiIDDBuf[1015];
3734 if (!uiMask || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) ||
3735 (aVendorLogs[i].ucLogPage == 0xE7 && uiMask == 1)) {
3736 bSize = 0;
3737 } else {
3738 bSize = (int)puiIDDBuf[1023];
3739 if (bSize % (16 * 1024))
3740 bSize += (16 * 1024) - (bSize % (16 * 1024));
3741 }
3742 dataBuffer = (unsigned char *)libnvme_alloc(bSize);
3743 if (bSize && dataBuffer) {
3744 memset(dataBuffer, 0, bSize);
3745 if (eModel == M5410 || eModel == M5407)
3746 err = NVMEGetLogPage(hdl,
3747 aVendorLogs[i].ucLogPage,
3748 dataBuffer, bSize, 0);
3749 else
3750 err = nvme_get_log_simple(hdl,
3751 aVendorLogs[i].ucLogPage,
3752 dataBuffer, bSize);
3753 }
3754 break;
3755 case 0xF7:
3756 case 0xF9:
3757 case 0xFC:
3758 case 0xFD:
3759 if (eModel == M51BX)
3760 (void)NVMEResetLog(hdl, aVendorLogs[i].ucLogPage,
3761 aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize);
3762
3763 default:
3764 bSize = aVendorLogs[i].nLogSize;
3765 dataBuffer = (unsigned char *)libnvme_alloc(bSize);
3766 if (!dataBuffer)
3767 break;
3768 memset(dataBuffer, 0, bSize);
3769 err = nvme_get_log_simple(hdl, aVendorLogs[i].ucLogPage,
3770 dataBuffer, bSize);
3771 maxSize = aVendorLogs[i].nMaxSize - bSize;
3772 while (!err && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) {
3773 sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage);
3774 WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg);
3775 err = nvme_get_log_simple(hdl,
3776 aVendorLogs[i].ucLogPage,
3777 dataBuffer, bSize);
3778 if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef))
3779 break;
3780 maxSize -= bSize;
3781 }
3782 break;
3783 }
3784
3785 if (!err && dataBuffer && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) {
3786 sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage);
3787 WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg);
3788 }
3789
3790 libnvme_free(dataBuffer);
3791 dataBuffer = NULL((void*)0);
3792 }
3793
3794 err = ZipAndRemoveDir(strMainDirName, cfg.package);
3795out:
3796 return err;
3797}
3798
3799#define MIN_LOG_SIZE512 512
3800static int micron_logpage_dir(int argc, char **argv, struct command *acmd,
3801 struct plugin *plugin)
3802{
3803 int err = -1;
3804 const char *desc = "List the supported log pages";
3805 enum eDriveModel model = UNKNOWN_MODEL;
3806 char logbuf[MIN_LOG_SIZE512];
3807 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3808 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3809 int i;
3810
3811 NVME_ARGS(opts)struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
3812
3813 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3814 if (err < 0)
3815 return err;
3816
3817 struct nvme_supported_logs {
3818 uint8_t log_id;
3819 uint8_t supported;
3820 char *desc;
3821 } log_list[] = {
3822 {0x00, 0, "Support Log Pages"},
3823 {0x01, 0, "Error Information"},
3824 {0x02, 0, "SMART / Health Information"},
3825 {0x03, 0, "Firmware Slot Information"},
3826 {0x04, 0, "Changed Namespace List"},
3827 {0x05, 0, "Commands Supported and Effects"},
3828 {0x06, 0, "Device Self Test"},
3829 {0x07, 0, "Telemetry Host-Initiated"},
3830 {0x08, 0, "Telemetry Controller-Initiated"},
3831 {0x09, 0, "Endurance Group Information"},
3832 {0x0A, 0, "Predictable Latency Per NVM Set"},
3833 {0x0B, 0, "Predictable Latency Event Aggregate"},
3834 {0x0C, 0, "Asymmetric Namespace Access"},
3835 {0x0D, 0, "Persistent Event Log"},
3836 {0x0E, 0, "Predictable Latency Event Aggregate"},
3837 {0x0F, 0, "Endurance Group Event Aggregate"},
3838 {0x10, 0, "Media Unit Status"},
3839 {0x11, 0, "Supported Capacity Configuration List"},
3840 {0x12, 0, "Feature Identifiers Supported and Effects"},
3841 {0x13, 0, "NVMe-MI Commands Supported and Effects"},
3842 {0x14, 0, "Command and Feature lockdown"},
3843 {0x15, 0, "Boot Partition"},
3844 {0x16, 0, "Rotational Media Information"},
3845 {0x70, 0, "Discovery"},
3846 {0x80, 0, "Reservation Notification"},
3847 {0x81, 0, "Sanitize Status"},
3848 {0xC0, 0, "SMART Cloud Health Log"},
3849 {0xC2, 0, "Firmware Activation History"},
3850 {0xC3, 0, "Latency Monitor Log"},
3851 };
3852
3853 printf("Supported log page list\nLog ID : Description\n");
3854 for (i = 0; i < ARRAY_SIZE(log_list)(sizeof(log_list) / sizeof((log_list)[0])); i++) {
3855 err = nvme_get_log_simple(hdl, log_list[i].log_id,
3856 &logbuf[0], MIN_LOG_SIZE512);
3857 if (err)
3858 continue;
3859 printf("%02Xh : %s\n", log_list[i].log_id, log_list[i].desc);
3860 }
3861
3862 return err;
3863}
3864
3865static int micron_cloud_boot_SSD_version(int argc, char **argv,
3866 struct command *command, struct plugin *plugin)
3867{
3868 const char *desc = "Prints HyperScale Boot Version";
3869 unsigned char logC0[C0_log_size512] = { 0 };
3870 struct nvme_id_ctrl ctrl;
3871 enum eDriveModel eModel = UNKNOWN_MODEL;
3872 int err = 0;
3873 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3874 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3875 struct format {
3876 char *fmt;
3877 };
3878 const char *fmt = "output format normal";
3879 struct format cfg = {
3880 .fmt = "normal",
3881 };
3882
3883 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
3884 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
3885
3886 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
3887 if (err < 0)
3888 return -1;
3889
3890 err = nvme_identify_ctrl(hdl, &ctrl);
3891 if (err == 0) {
3892 if (ctrl.vs[536] != MICRON_CUST_ID_GG0x16) {
3893 printf(
3894 "cloud-boot-SSD-version option is not supported for specified drive\n"
3895 );
3896 goto out;
3897 }
3898 } else {
3899 printf("Error %d retrieving controller identification data\n", err);
3900 goto out;
3901 }
3902
3903 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
3904 if (err == 0) {
3905 __u16 major, minor;
3906
3907 major = *((__u16 *)(logC0+300));
3908 minor = *((__u16 *)(logC0+302));
3909
3910 printf("HyperScale Boot Version Spec.%x.%x\n", le16_to_cpu(major)
3911 , le16_to_cpu(minor));
3912 } else {
3913 nvme_show_err(err, "Error retrieving extended smart log 0xC0 for the drive");
3914 goto out;
3915 }
3916out:
3917 return err;
3918}
3919
3920static int micron_device_waf(int argc, char **argv, struct command *acmd,
3921 struct plugin *plugin)
3922{
3923 const char *desc = "Prints device Write Amplification Factor(WAF)";
3924 unsigned char logC0[C0_log_size512] = { 0 };
3925 struct nvme_id_ctrl ctrl;
3926 struct nvme_smart_log smart_log;
3927 enum eDriveModel eModel = UNKNOWN_MODEL;
3928 int err = 0;
3929 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3930 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3931
3932 long double tlc_units_written, slc_units_written;
3933 long double data_units_written, write_amplification_factor;
3934
3935 struct format {
3936 char *fmt;
3937 };
3938
3939 const char *fmt = "output format normal";
3940
3941 struct format cfg = {
3942 .fmt = "normal",
3943 };
3944
3945 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
3946 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
3947
3948 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
3949 if (err < 0)
3950 return -1;
3951
3952 err = nvme_identify_ctrl(hdl, &ctrl);
3953 if (err == 0) {
3954 if (ctrl.vs[536] != MICRON_CUST_ID_GG0x16) {
3955 printf("vs-device-waf option is not supported for specified drive\n");
3956 goto out;
3957 }
3958 } else {
3959 printf("Error %d retrieving controller identification data\n", err);
3960 goto out;
3961 }
3962
3963 err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log);
3964 if (err != 0) {
3965 fprintf(stderrstderr, "nvme_smart_log() failed, err = %d\n", err);
3966 goto out;
3967 }
3968
3969 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
3970 if (err != 0) {
3971 fprintf(stderrstderr, "Failed to get extended smart log, err = %d\n", err);
3972 goto out;
3973 }
3974
3975 data_units_written = int128_to_double(smart_log.data_units_written);
3976 tlc_units_written = int128_to_double((__u8 *)logC0);
3977 slc_units_written = int128_to_double((__u8 *)(logC0+16));
3978 write_amplification_factor = (data_units_written/(tlc_units_written + slc_units_written));
3979 printf("Write Amplification Factor %.0Lf\n", write_amplification_factor);
3980
3981out:
3982 return err;
3983}
3984
3985static int micron_cloud_log(int argc, char **argv, struct command *acmd,
3986 struct plugin *plugin)
3987{
3988 const char *desc = "Retrieve Smart or Extended Smart Health log for the given device ";
3989 unsigned int logC0[C0_log_size512/sizeof(int)] = { 0 };
3990 struct nvme_id_ctrl ctrl;
3991 enum eDriveModel eModel = UNKNOWN_MODEL;
3992 int err = 0;
3993 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3994 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3995 bool_Bool is_json = true1;
3996 struct format {
3997 char *fmt;
3998 };
3999 const char *fmt = "output format normal|json";
4000 struct format cfg = {
4001 .fmt = "json",
4002 };
4003
4004 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
4005 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
4006
4007 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4008 if (err < 0)
4009 return -1;
4010
4011 if (strcmp(cfg.fmt, "normal") == 0)
4012 is_json = false0;
4013
4014 /* check for models that support 0xC0 log */
4015 if (eModel != M51CX) {
4016 printf("Unsupported drive model for vs-cloud-log commmand\n");
4017 err = -1;
4018 goto out;
4019 }
4020
4021 err = nvme_identify_ctrl(hdl, &ctrl);
4022 if (err == 0) {
4023 if (ctrl.vs[536] != MICRON_CUST_ID_GG0x16) {
4024 printf("vs-cloud-log option is not supported for specified drive\n");
4025 goto out;
4026 }
4027 } else {
4028 printf("Error %d retrieving controller identification data\n", err);
4029 goto out;
4030 }
4031
4032 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
4033 if (err == 0)
4034 print_hyperscale_cloud_health_log((__u8 *)logC0, is_json);
4035 else
4036 printf("Unable to retrieve extended smart log 0xC0 for the drive\n");
4037
4038out:
4039 if (err > 0)
4040 nvme_show_status(err);
4041 return err;
4042}
4043
4044/* Extended SMART log structure with Micron-specific fields in reserved area */
4045struct micron_smart_log_ext {
4046 struct nvme_smart_log base;
4047 /* Access vendor-specific fields via rsvd232 overlay */
4048};
4049
4050static inline __u64 get_smart_olec(struct nvme_smart_log *smart)
4051{
4052 return le64_to_cpu(smart->op_lifetime_energy_consumed);
4053}
4054
4055static inline __u32 get_smart_ipm(struct nvme_smart_log *smart)
4056{
4057 return le32_to_cpu(smart->interval_power_measurement);
4058}
4059
4060static void print_micron_health_log_normal(struct nvme_smart_log *smart,
4061 const char *devname)
4062{
4063 __u16 temp = smart->temperature[1] << 8 | smart->temperature[0];
4064 __u64 olec = get_smart_olec(smart);
4065 __u32 ipm = get_smart_ipm(smart);
4066 int i;
4067
4068 printf("SMART/Health Information Log for %s\n", devname);
4069 printf("========================================\n");
4070
4071 printf("Critical Warning : 0x%02x\n",
4072 smart->critical_warning);
4073 if (smart->critical_warning) {
4074 if (smart->critical_warning & 0x01)
4075 printf(" - Available spare below threshold\n");
4076 if (smart->critical_warning & 0x02)
4077 printf(" - Temperature threshold exceeded\n");
4078 if (smart->critical_warning & 0x04)
4079 printf(" - NVM subsystem reliability degraded\n");
4080 if (smart->critical_warning & 0x08)
4081 printf(" - Media placed in read-only mode\n");
4082 if (smart->critical_warning & 0x10)
4083 printf(" - Volatile memory backup failed\n");
4084 if (smart->critical_warning & 0x20)
4085 printf(" - PMR read-only or unreliable\n");
4086 }
4087
4088 printf("Composite Temperature : %u K (%d C)\n",
4089 temp, temp ? temp - 273 : 0);
4090 printf("Available Spare : %u%%\n", smart->avail_spare);
4091 printf("Available Spare Threshold : %u%%\n", smart->spare_thresh);
4092 printf("Percentage Used : %u%%\n", smart->percent_used);
4093 printf("Endurance Grp Critical Warn : 0x%02x\n",
4094 smart->endu_grp_crit_warn_sumry);
4095
4096 printf("Data Units Read : %s\n",
4097 uint128_t_to_string(le128_to_cpu(smart->data_units_read)));
4098 printf("Data Units Written : %s\n",
4099 uint128_t_to_string(le128_to_cpu(smart->data_units_written)));
4100 printf("Host Read Commands : %s\n",
4101 uint128_t_to_string(le128_to_cpu(smart->host_reads)));
4102 printf("Host Write Commands : %s\n",
4103 uint128_t_to_string(le128_to_cpu(smart->host_writes)));
4104 printf("Controller Busy Time : %s min\n",
4105 uint128_t_to_string(le128_to_cpu(smart->ctrl_busy_time)));
4106 printf("Power Cycles : %s\n",
4107 uint128_t_to_string(le128_to_cpu(smart->power_cycles)));
4108 printf("Power On Hours : %s\n",
4109 uint128_t_to_string(le128_to_cpu(smart->power_on_hours)));
4110 printf("Unsafe Shutdowns : %s\n",
4111 uint128_t_to_string(le128_to_cpu(smart->unsafe_shutdowns)));
4112 printf("Media Errors : %s\n",
4113 uint128_t_to_string(le128_to_cpu(smart->media_errors)));
4114 printf("Num Error Log Entries : %s\n",
4115 uint128_t_to_string(le128_to_cpu(smart->num_err_log_entries)));
4116
4117 printf("Warning Comp Temp Time : %u min\n",
4118 le32_to_cpu(smart->warning_temp_time));
4119 printf("Critical Comp Temp Time : %u min\n",
4120 le32_to_cpu(smart->critical_comp_time));
4121
4122 for (i = 0; i < 8; i++) {
4123 __u16 ts = le16_to_cpu(smart->temp_sensor[i]);
4124
4125 if (ts)
4126 printf("Temperature Sensor %d : %u K (%d C)\n",
4127 i + 1, ts, ts - 273);
4128 }
4129
4130 printf("Thm Temp 1 Trans Count : %u\n",
4131 le32_to_cpu(smart->thm_temp1_trans_count));
4132 printf("Thm Temp 2 Trans Count : %u\n",
4133 le32_to_cpu(smart->thm_temp2_trans_count));
4134 printf("Thm Temp 1 Total Time : %u sec\n",
4135 le32_to_cpu(smart->thm_temp1_total_time));
4136 printf("Thm Temp 2 Total Time : %u sec\n",
4137 le32_to_cpu(smart->thm_temp2_total_time));
4138
4139 /* Micron-specific extended fields */
4140 printf("OLEC (Energy) : %llu\n",
4141 (unsigned long long)olec);
4142 printf("Interval Power Measurement : %u\n", ipm);
4143}
4144
4145static void print_micron_health_log_json(struct nvme_smart_log *smart,
4146 const char *devname)
4147{
4148 __u16 temp = smart->temperature[1] << 8 | smart->temperature[0];
4149 __u64 olec = get_smart_olec(smart);
4150 __u32 ipm = get_smart_ipm(smart);
4151 struct json_object *root;
4152 int i;
4153
4154 root = json_create_object()json_object_new_object();
4155
4156 json_object_add_value_string(root, "device", devname);
4157 json_object_add_value_int(root, "critical_warning",json_object_object_add(root, "critical_warning", json_object_new_int
(smart->critical_warning))
4158 smart->critical_warning)json_object_object_add(root, "critical_warning", json_object_new_int
(smart->critical_warning))
;
4159 json_object_add_value_int(root, "temperature_kelvin", temp)json_object_object_add(root, "temperature_kelvin", json_object_new_int
(temp))
;
4160 json_object_add_value_int(root, "temperature_celsius",json_object_object_add(root, "temperature_celsius", json_object_new_int
(temp ? temp - 273 : 0))
4161 temp ? temp - 273 : 0)json_object_object_add(root, "temperature_celsius", json_object_new_int
(temp ? temp - 273 : 0))
;
4162 json_object_add_value_int(root, "avail_spare", smart->avail_spare)json_object_object_add(root, "avail_spare", json_object_new_int
(smart->avail_spare))
;
4163 json_object_add_value_int(root, "spare_thresh", smart->spare_thresh)json_object_object_add(root, "spare_thresh", json_object_new_int
(smart->spare_thresh))
;
4164 json_object_add_value_int(root, "percent_used", smart->percent_used)json_object_object_add(root, "percent_used", json_object_new_int
(smart->percent_used))
;
4165 json_object_add_value_int(root, "endurance_grp_crit_warn",json_object_object_add(root, "endurance_grp_crit_warn", json_object_new_int
(smart->endu_grp_crit_warn_sumry))
4166 smart->endu_grp_crit_warn_sumry)json_object_object_add(root, "endurance_grp_crit_warn", json_object_new_int
(smart->endu_grp_crit_warn_sumry))
;
4167
4168 json_object_add_value_string(root, "data_units_read",
4169 uint128_t_to_string(le128_to_cpu(smart->data_units_read)));
4170 json_object_add_value_string(root, "data_units_written",
4171 uint128_t_to_string(le128_to_cpu(smart->data_units_written)));
4172 json_object_add_value_string(root, "host_reads",
4173 uint128_t_to_string(le128_to_cpu(smart->host_reads)));
4174 json_object_add_value_string(root, "host_writes",
4175 uint128_t_to_string(le128_to_cpu(smart->host_writes)));
4176 json_object_add_value_string(root, "ctrl_busy_time",
4177 uint128_t_to_string(le128_to_cpu(smart->ctrl_busy_time)));
4178 json_object_add_value_string(root, "power_cycles",
4179 uint128_t_to_string(le128_to_cpu(smart->power_cycles)));
4180 json_object_add_value_string(root, "power_on_hours",
4181 uint128_t_to_string(le128_to_cpu(smart->power_on_hours)));
4182 json_object_add_value_string(root, "unsafe_shutdowns",
4183 uint128_t_to_string(le128_to_cpu(smart->unsafe_shutdowns)));
4184 json_object_add_value_string(root, "media_errors",
4185 uint128_t_to_string(le128_to_cpu(smart->media_errors)));
4186 json_object_add_value_string(root, "num_err_log_entries",
4187 uint128_t_to_string(le128_to_cpu(smart->num_err_log_entries)));
4188
4189 json_object_add_value_uint(root, "warning_temp_time",json_object_object_add(root, "warning_temp_time", json_object_new_uint64
(le32_to_cpu(smart->warning_temp_time)))
4190 le32_to_cpu(smart->warning_temp_time))json_object_object_add(root, "warning_temp_time", json_object_new_uint64
(le32_to_cpu(smart->warning_temp_time)))
;
4191 json_object_add_value_uint(root, "critical_comp_time",json_object_object_add(root, "critical_comp_time", json_object_new_uint64
(le32_to_cpu(smart->critical_comp_time)))
4192 le32_to_cpu(smart->critical_comp_time))json_object_object_add(root, "critical_comp_time", json_object_new_uint64
(le32_to_cpu(smart->critical_comp_time)))
;
4193
4194 for (i = 0; i < 8; i++) {
4195 __u16 ts = le16_to_cpu(smart->temp_sensor[i]);
4196 char key[32];
4197
4198 if (ts) {
4199 sprintf(key, "temp_sensor_%d", i + 1);
4200 json_object_add_value_int(root, key, ts - 273)json_object_object_add(root, key, json_object_new_int(ts - 273
))
;
4201 }
4202 }
4203
4204 json_object_add_value_uint(root, "thm_temp1_trans_count",json_object_object_add(root, "thm_temp1_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_trans_count)))
4205 le32_to_cpu(smart->thm_temp1_trans_count))json_object_object_add(root, "thm_temp1_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_trans_count)))
;
4206 json_object_add_value_uint(root, "thm_temp2_trans_count",json_object_object_add(root, "thm_temp2_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_trans_count)))
4207 le32_to_cpu(smart->thm_temp2_trans_count))json_object_object_add(root, "thm_temp2_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_trans_count)))
;
4208 json_object_add_value_uint(root, "thm_temp1_total_time",json_object_object_add(root, "thm_temp1_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_total_time)))
4209 le32_to_cpu(smart->thm_temp1_total_time))json_object_object_add(root, "thm_temp1_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_total_time)))
;
4210 json_object_add_value_uint(root, "thm_temp2_total_time",json_object_object_add(root, "thm_temp2_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_total_time)))
4211 le32_to_cpu(smart->thm_temp2_total_time))json_object_object_add(root, "thm_temp2_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_total_time)))
;
4212
4213 /* Micron-specific extended fields */
4214 json_object_add_value_uint64(root, "olec", olec)json_object_object_add(root, "olec", json_object_new_uint64(olec
))
;
4215 json_object_add_value_uint(root, "ipm", ipm)json_object_object_add(root, "ipm", json_object_new_uint64(ipm
))
;
4216
4217 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
4218 printf("\n");
4219 json_free_object(root)json_object_put(root);
4220}
4221
4222static int micron_health_info(int argc, char **argv, struct command *acmd,
4223 struct plugin *plugin)
4224{
4225 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4226 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4227 const char *desc = "Retrieve SMART/Health log for Micron drives";
4228 const char *fmt = "output format normal|json";
4229 enum eDriveModel eModel = UNKNOWN_MODEL;
4230 struct nvme_smart_log smart_log = { 0 };
4231 bool_Bool is_json = false0;
4232 int err = 0;
4233 struct format {
4234 char *fmt;
4235 };
4236 struct format cfg = {
4237 .fmt = "normal",
4238 };
4239
4240 NVME_ARGS(opts,struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
4241 OPT_FMT("format", 'f', &cfg.fmt, fmt))struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"format", 'f', "FMT", CFG_STRING, &cfg.fmt, 1, fmt
, 0, }, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
4242
4243 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4244 if (err < 0)
4245 return err;
4246
4247 if (eModel == UNKNOWN_MODEL)
4248 fprintf(stderrstderr, "WARNING: Unknown drive model\n");
4249
4250 if (!strcmp(cfg.fmt, "json"))
4251 is_json = true1;
4252
4253 err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log);
4254 if (err) {
4255 fprintf(stderrstderr, "Failed to get SMART log: %s\n",
4256 libnvme_strerror(err));
4257 return err;
4258 }
4259
4260 if (is_json)
4261 print_micron_health_log_json(&smart_log, argv[optind]);
4262 else
4263 print_micron_health_log_normal(&smart_log, argv[optind]);
4264
4265 return 0;
4266}
4267
4268/*
4269 * Identify Controller field offsets for Micron-specific fields
4270 * PMS: Power Measurement Support - bit 21 of CTRATT
4271 */
4272#define CTRATT_PMS_BIT21 21
4273
4274static inline __u16 get_id_ctrl_ipmsr(struct nvme_id_ctrl *ctrl)
4275{
4276 __le16 *p = (__le16 *)&ctrl->ipmsr;
4277
4278 return le16_to_cpu(*p);
4279}
4280
4281static inline __u16 get_id_ctrl_msmt(struct nvme_id_ctrl *ctrl)
4282{
4283 __le16 *p = (__le16 *)&ctrl->msmt;
4284
4285 return le16_to_cpu(*p);
4286}
4287
4288static inline bool_Bool get_id_ctrl_pms(struct nvme_id_ctrl *ctrl)
4289{
4290 return (le32_to_cpu(ctrl->ctratt) >> CTRATT_PMS_BIT21) & 0x1;
4291}
4292
4293/* Micron vendor-specific id-ctrl fields display */
4294static void micron_id_ctrl_vs(__u8 *vs, struct json_object *root)
4295{
4296 /* Cast back to get full ctrl structure for our extended fields */
4297 struct nvme_id_ctrl *ctrl =
4298 (struct nvme_id_ctrl *)(vs - offsetof(struct nvme_id_ctrl, vs)__builtin_offsetof(struct nvme_id_ctrl, vs));
4299 __u16 ipmsr = get_id_ctrl_ipmsr(ctrl);
4300 __u16 msmt = get_id_ctrl_msmt(ctrl);
4301 bool_Bool pms = get_id_ctrl_pms(ctrl);
4302
4303 if (root) {
4304 /* JSON output */
4305 json_object_add_value_int(root, "pms", pms ? 1 : 0)json_object_object_add(root, "pms", json_object_new_int(pms ?
1 : 0))
;
4306 json_object_add_value_uint(root, "ipmsr", ipmsr)json_object_object_add(root, "ipmsr", json_object_new_uint64(
ipmsr))
;
4307 json_object_add_value_uint(root, "msmt", msmt)json_object_object_add(root, "msmt", json_object_new_uint64(msmt
))
;
4308 } else {
4309 /* Normal output */
4310 printf("pms : %u\n", pms ? 1 : 0);
4311 printf("ipmsr : %u\n", ipmsr);
4312 printf("msmt : %u\n", msmt);
4313 }
4314}
4315
4316static int micron_id_ctrl(int argc, char **argv, struct command *acmd,
4317 struct plugin *plugin)
4318{
4319 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4320 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4321 const char *desc = "Identify Controller with Micron vendor fields";
4322 enum eDriveModel eModel = UNKNOWN_MODEL;
4323 struct nvme_id_ctrl ctrl = { 0 };
4324 nvme_print_flags_t flags;
4325 int err = 0;
4326
4327 NVME_ARGS(opts)struct argconfig_commandline_options opts[] = { {"", 0, ((void
*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0, "Options", 0, ((void
*)0)}, {"", 0, ((void*)0), CFG_GROUP_SEPARATOR, ((void*)0), 0
, "Global options", 0, ((void*)0)}, {"verbose", 'v', "NUM", CFG_INCREMENT
, &nvme_args.verbose, 0, "Increase output verbosity", 0, }
, {"output-format", 'o', "FMT", CFG_STRING, &nvme_args.output_format
, 1, "Output format: normal|json|binary|tabular", 0, }, {"timeout"
, 0, "NUM", CFG_POSITIVE, &nvme_args.timeout, 1, "timeout value, in milliseconds"
, 0, }, {"dry-run", 0, ((void*)0), CFG_FLAG, &nvme_args.dry_run
, 0, "show command instead of executing", 0, }, {"no-retries"
, 0, ((void*)0), CFG_FLAG, &nvme_args.no_retries, 0, "disable retry logic on errors"
, 0, }, {"no-ioctl-probing", 0, ((void*)0), CFG_FLAG, &nvme_args
.no_ioctl_probing, 0, "disable 64-bit IOCTL support probing",
0, }, {"output-format-version", 0, "NUM", CFG_POSITIVE, &
nvme_args.output_format_ver, 1, "output format version: 1|2",
0, }, {"human-readable", 'H', ((void*)0), CFG_FLAG, &nvme_args
.verbose, 0, ((void*)0), 0, ((void*)0), 1}, { ((void*)0) } }
;
4328
4329 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4330 if (err < 0)
4331 return err;
4332
4333 if (eModel == UNKNOWN_MODEL) {
4334 fprintf(stderrstderr,
4335 "WARNING: Drive not recognized as Micron, proceeding anyway\n");
4336 }
4337
4338 err = validate_output_format(nvme_args.output_format, &flags);
4339 if (err < 0) {
4340 fprintf(stderrstderr, "Invalid output format\n");
4341 return err;
4342 }
4343
4344 err = nvme_identify_ctrl(hdl, &ctrl);
4345 if (err) {
4346 fprintf(stderrstderr, "identify controller failed: %s\n",
4347 libnvme_strerror(err));
4348 return err;
4349 }
4350
4351 nvme_show_id_ctrl(&ctrl, libnvme_transport_handle_get_name(hdl),
4352 flags, micron_id_ctrl_vs);
4353
4354 return 0;
4355}