12#define __STDC_FORMAT_MACROS 1
16const char *
nvmeprint_cvsid =
"$Id: nvmeprint.cpp 5630 2024-10-23 17:15:56Z chrfranke $"
34 for (
int i = 0; i < 16; i++) {
43static const char *
le128_to_str(
char (& str)[64], uint64_t hi, uint64_t lo,
unsigned bytes_per_unit)
49 if (lo && bytes_per_unit && lo < 0xffffffffffffffffULL / bytes_per_unit) {
51 str[i++] =
' '; str[i++] =
'[';
54 str[i++] =
']'; str[i] = 0;
71static const char *
le128_to_str(
char (& str)[64],
const unsigned char (& val)[16],
72 unsigned bytes_per_unit = 0)
74 uint64_t hi = val[15];
75 for (
int i = 15-1; i >= 8; i--) {
76 hi <<= 8; hi += val[i];
79 for (
int i = 7-1; i >= 0; i--) {
80 lo <<= 8; lo += val[i];
86static const char *
lbacap_to_str(
char (& str)[64], uint64_t lba_cnt,
int lba_bits)
88 return le128_to_str(str, (lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits), 1);
102 str[0] =
'-', str[1] = 0;
104 snprintf(str,
sizeof(str),
"%d Celsius", k - 273);
109 unsigned nsid,
bool show_all)
113 jglb[
"model_name"] = buf;
116 jglb[
"serial_number"] = buf;
120 jglb[
"firmware_version"] = buf;
123 if (show_all || id_ctrl.
vid != id_ctrl.
ssvid) {
124 jout(
"PCI Vendor ID: 0x%04x\n", id_ctrl.
vid);
125 jout(
"PCI Vendor Subsystem ID: 0x%04x\n", id_ctrl.
ssvid);
128 jout(
"PCI Vendor/Subsystem ID: 0x%04x\n", id_ctrl.
vid);
130 jglb[
"nvme_pci_vendor"][
"id"] = id_ctrl.
vid;
131 jglb[
"nvme_pci_vendor"][
"subsystem_id"] = id_ctrl.
ssvid;
133 jout(
"IEEE OUI Identifier: 0x%02x%02x%02x\n",
140 jglb[
"nvme_total_capacity"].set_unsafe_le128(id_ctrl.
tnvmcap);
142 jglb[
"nvme_unallocated_capacity"].set_unsafe_le128(id_ctrl.
unvmcap);
149 int i = snprintf(buf,
sizeof(buf),
"%u.%u", id_ctrl.
ver >> 16, (id_ctrl.
ver >> 8) & 0xff);
150 if (i > 0 && (id_ctrl.
ver & 0xff))
151 snprintf(buf+i,
sizeof(buf)-i,
".%u", id_ctrl.
ver & 0xff);
154 snprintf(buf,
sizeof(buf),
"<1.2");
155 jout(
"NVMe Version: %s\n", buf);
156 jglb[
"nvme_version"][
"string"] = buf;
157 jglb[
"nvme_version"][
"value"] = id_ctrl.
ver;
160 jout(
"Number of Namespaces: %u\n", id_ctrl.
nn);
161 jglb[
"nvme_number_of_namespaces"] = id_ctrl.
nn;
164 const char * align = &(
" "[
nsid < 10 ? 0 : (
nsid < 100 ? 1 : 2)]);
165 int fmt_lba_bits = id_ns.
lbaf[id_ns.
flbas & 0xf].
ds;
171 if (show_all || id_ns.
ncap != id_ns.
nsze || (id_ns.
nsfeat & 0x01)) {
172 jout(
"Namespace %u Size: %s%s\n",
nsid, align,
174 jout(
"Namespace %u Capacity: %s%s\n",
nsid, align,
178 jout(
"Namespace %u Size/Capacity: %s%s\n",
nsid, align,
186 if (show_all || id_ns.
nuse != id_ns.
ncap || (id_ns.
nsfeat & 0x01))
187 jout(
"Namespace %u Utilization: %s%s\n",
nsid, align,
191 jout(
"Namespace %u Formatted LBA Size: %s%u\n",
nsid, align, (1U << fmt_lba_bits));
192 jrns[
"formatted_lba_size"] = (1U << fmt_lba_bits);
193 jglb[
"logical_block_size"] = (1U << fmt_lba_bits);
196 jout(
"Namespace %u IEEE EUI-64: %s%02x%02x%02x %02x%02x%02x%02x%02x\n",
205 jglb[
"smart_support"] += { {
"available",
true}, {
"enabled",
true} };
211static const char *
format_power(
char (& str)[16],
unsigned power,
unsigned scale)
213 switch (scale & 0x3) {
215 str[0] =
'-'; str[1] =
' '; str[2] = 0;
break;
217 snprintf(str,
sizeof(str),
"%u.%04uW", power / 10000, power % 10000);
break;
219 snprintf(str,
sizeof(str),
"%u.%02uW", power / 100, power % 100);
break;
221 str[0] =
'?'; str[1] = 0;
break;
227 unsigned nsid,
bool show_all)
232 pout(
"Firmware Updates (0x%02x): %d Slot%s%s%s%s%s\n", id_ctrl.
frmw,
233 ((id_ctrl.
frmw >> 1) & 0x7), (((id_ctrl.
frmw >> 1) & 0x7) != 1 ?
"s" :
""),
234 ((id_ctrl.
frmw & 0x01) ?
", Slot 1 R/O" :
""),
235 ((id_ctrl.
frmw & 0x10) ?
", no Reset required" :
""),
236 ((id_ctrl.
frmw & 0x20) ?
", multiple detected" :
""),
237 ((id_ctrl.
frmw & ~0x3f) ?
", *Other*" :
""));
239 if (show_all || id_ctrl.
oacs)
240 pout(
"Optional Admin Commands (0x%04x): %s%s%s%s%s%s%s%s%s%s%s%s%s\n", id_ctrl.
oacs,
241 (!id_ctrl.
oacs ?
" -" :
""),
242 ((id_ctrl.
oacs & 0x0001) ?
" Security" :
""),
243 ((id_ctrl.
oacs & 0x0002) ?
" Format" :
""),
244 ((id_ctrl.
oacs & 0x0004) ?
" Frmw_DL" :
""),
245 ((id_ctrl.
oacs & 0x0008) ?
" NS_Mngmt" :
""),
246 ((id_ctrl.
oacs & 0x0010) ?
" Self_Test" :
""),
247 ((id_ctrl.
oacs & 0x0020) ?
" Directvs" :
""),
248 ((id_ctrl.
oacs & 0x0040) ?
" MI_Snd/Rec" :
""),
249 ((id_ctrl.
oacs & 0x0080) ?
" Vrt_Mngmt" :
""),
250 ((id_ctrl.
oacs & 0x0100) ?
" Drbl_Bf_Cfg" :
""),
251 ((id_ctrl.
oacs & 0x0200) ?
" Get_LBA_Sts" :
""),
252 ((id_ctrl.
oacs & 0x0400) ?
" Lockdown" :
""),
253 ((id_ctrl.
oacs & ~0x07ff) ?
" *Other*" :
""));
255 if (show_all || id_ctrl.
oncs)
256 pout(
"Optional NVM Commands (0x%04x): %s%s%s%s%s%s%s%s%s%s%s\n", id_ctrl.
oncs,
257 (!id_ctrl.
oncs ?
" -" :
""),
258 ((id_ctrl.
oncs & 0x0001) ?
" Comp" :
""),
259 ((id_ctrl.
oncs & 0x0002) ?
" Wr_Unc" :
""),
260 ((id_ctrl.
oncs & 0x0004) ?
" DS_Mngmt" :
""),
261 ((id_ctrl.
oncs & 0x0008) ?
" Wr_Zero" :
""),
262 ((id_ctrl.
oncs & 0x0010) ?
" Sav/Sel_Feat" :
""),
263 ((id_ctrl.
oncs & 0x0020) ?
" Resv" :
""),
264 ((id_ctrl.
oncs & 0x0040) ?
" Timestmp" :
""),
265 ((id_ctrl.
oncs & 0x0080) ?
" Verify" :
""),
266 ((id_ctrl.
oncs & 0x0100) ?
" Copy" :
""),
267 ((id_ctrl.
oncs & ~0x01ff) ?
" *Other*" :
""));
269 if (show_all || id_ctrl.
lpa)
270 pout(
"Log Page Attributes (0x%02x): %s%s%s%s%s%s%s%s%s\n", id_ctrl.
lpa,
271 (!id_ctrl.
lpa ?
" -" :
""),
272 ((id_ctrl.
lpa & 0x01) ?
" S/H_per_NS" :
""),
273 ((id_ctrl.
lpa & 0x02) ?
" Cmd_Eff_Lg" :
""),
274 ((id_ctrl.
lpa & 0x04) ?
" Ext_Get_Lg" :
""),
275 ((id_ctrl.
lpa & 0x08) ?
" Telmtry_Lg" :
""),
276 ((id_ctrl.
lpa & 0x10) ?
" Pers_Ev_Lg" :
""),
277 ((id_ctrl.
lpa & 0x20) ?
" Log0_FISE_MI" :
""),
278 ((id_ctrl.
lpa & 0x40) ?
" Telmtry_Ar_4" :
""),
279 ((id_ctrl.
lpa & ~0x7f) ?
" *Other*" :
""));
282 pout(
"Maximum Data Transfer Size: %u Pages\n", (1U << id_ctrl.
mdts));
284 pout(
"Maximum Data Transfer Size: -\n");
288 if (show_all || id_ctrl.
wctemp)
290 if (show_all || id_ctrl.
cctemp)
297 const char * align = &(
" "[
nsid < 10 ? 0 : (
nsid < 100 ? 1 : 2)]);
298 pout(
"Namespace %u Features (0x%02x): %s%s%s%s%s%s%s%s\n",
nsid, id_ns.
nsfeat, align,
299 (!id_ns.
nsfeat ?
" -" :
""),
300 ((id_ns.
nsfeat & 0x01) ?
" Thin_Prov" :
""),
301 ((id_ns.
nsfeat & 0x02) ?
" NA_Fields" :
""),
302 ((id_ns.
nsfeat & 0x04) ?
" Dea/Unw_Error" :
""),
303 ((id_ns.
nsfeat & 0x08) ?
" No_ID_Reuse" :
""),
304 ((id_ns.
nsfeat & 0x10) ?
" NP_Fields" :
""),
305 ((id_ns.
nsfeat & ~0x1f) ?
" *Other*" :
""));
309 pout(
"\nSupported Power States\n");
310 pout(
"St Op Max Active Idle RL RT WL WT Ent_Lat Ex_Lat\n");
311 for (
int i = 0; i <= id_ctrl.
npss && i < 32; i++) {
312 char p1[16], p2[16], p3[16];
314 pout(
"%2d %c %9s %8s %8s %3d %2d %2d %2d %8u %7u\n", i,
315 ((ps.
flags & 0x02) ?
'-' :
'+'),
326 pout(
"\nSupported LBA Sizes (NSID 0x%x)\n",
nsid);
327 pout(
"Id Fmt Data Metadt Rel_Perf\n");
328 for (
int i = 0; i <= id_ns.
nlbaf && i < 16; i++) {
330 pout(
"%2d %c %7u %7d %9d\n", i, (i == id_ns.
flbas ?
'+' :
'-'),
331 (1U << lba.
ds), lba.
ms, lba.
rp);
338 jout(
"SMART overall-health self-assessment test result: %s\n",
339 (!
w ?
"PASSED" :
"FAILED!"));
340 jglb[
"smart_status"][
"passed"] = !
w;
347 jout(
"- available spare has fallen below threshold\n");
348 jref[
"spare_below_threshold"] = !!(
w & 0x01);
350 jout(
"- temperature is above or below threshold\n");
351 jref[
"temperature_above_or_below_threshold"] = !!(
w & 0x02);
353 jout(
"- NVM subsystem reliability has been degraded\n");
354 jref[
"reliability_degraded"] = !!(
w & 0x04);
356 jout(
"- media has been placed in read only mode\n");
357 jref[
"media_read_only"] = !!(
w & 0x08);
359 jout(
"- volatile memory backup device has failed\n");
360 jref[
"volatile_memory_backup_failed"] = !!(
w & 0x10);
362 jout(
"- persistent memory region has become read-only or unreliable\n");
363 jref[
"persistent_memory_region_unreliable"] = !!(
w & 0x20);
365 jout(
"- unknown critical warning(s) (0x%02x)\n",
w & ~0x3f);
366 jref[
"other"] =
w & ~0x3f;
377 jout(
"SMART/Health Information (NVMe Log 0x02, NSID 0x%x)\n",
nsid);
384 jref[
"temperature"] = k - 273;
385 jglb[
"temperature"][
"current"] = k - 273;
391 jref[
"available_spare_threshold"] = smart_log.
spare_thresh;
428 for (
int i = 0; i < 8; i++) {
431 jout(
"Temperature Sensor %d: %s\n", i + 1,
434 jref[
"temperature_sensors"][i] = k - 273;
449 unsigned read_entries,
unsigned max_entries)
454 jout(
"Error Information (NVMe Log 0x01, %u of %u entries)\n",
455 read_entries, max_entries);
458 unsigned valid_entries = read_entries;
459 while (valid_entries && !error_log[valid_entries-1].error_count)
462 unsigned unread_entries = 0;
463 if (valid_entries == read_entries && read_entries < max_entries)
464 unread_entries = max_entries - read_entries;
466 {
"size", max_entries },
467 {
"read", read_entries },
468 {
"unread", unread_entries },
471 if (!valid_entries) {
472 jout(
"No Errors Logged\n\n");
476 jout(
"Num ErrCount SQId CmdId Status PELoc LBA NSID VS Message\n");
478 for (
unsigned i = 0; i < valid_entries; i++) {
486 jout(
" - [%d unused entr%s]\n", unused, (unused == 1 ?
"y" :
"ies"));
492 const char * msg =
"-";
char msgbuf[64]{};
493 char sq[16] =
"-", cm[16] =
"-", st[16] =
"-", pe[16] =
"-";
494 char lb[32] =
"-", ns[16] =
"-", vs[8] =
"-";
495 if (e.
sqid != 0xffff) {
496 snprintf(sq,
sizeof(sq),
"%d", e.
sqid);
497 jrefi[
"submission_queue_id"] = e.
sqid;
499 if (e.
cmdid != 0xffff) {
500 snprintf(cm,
sizeof(cm),
"0x%04x", e.
cmdid);
501 jrefi[
"command_id"] = e.
cmdid;
510 {
"do_not_retry", !!(
s & 0x4000) },
511 {
"status_code_type", (
s >> 8) & 0x7 },
512 {
"status_code" , (uint8_t)
s },
522 if (e.
lba != 0xffffffffffffffffULL) {
523 snprintf(lb,
sizeof(lb),
"%" PRIu64, e.
lba);
527 snprintf(ns,
sizeof(ns),
"%u", e.
nsid);
528 jrefi[
"nsid"] = e.
nsid;
531 snprintf(vs,
sizeof(vs),
"0x%02x", e.
vs);
532 jrefi[
"vendor_specific"] = e.
vs;
536 jout(
"%3u %10" PRIu64
" %5s %7s %7s %6s %12s %5s %5s %s\n",
537 i, e.
error_count, sq, cm, st, pe, lb, ns, vs, msg);
541 jout(
"... (%u entries not read)\n", unread_entries);
550 jout(
"Self-test Log (NVMe Log 0x06, NSID 0x%x)\n",
nsid);
553 const char *
s;
char buf[32];
555 case 0x0:
s =
"No self-test in progress";
break;
556 case 0x1:
s =
"Short self-test in progress";
break;
557 case 0x2:
s =
"Extended self-test in progress";
break;
558 case 0xe:
s =
"Vendor specific self-test in progress";
break;
559 default: snprintf(buf,
sizeof(buf),
"Unknown status (0x%x)",
563 jout(
"Self-test status: %s",
s);
564 jref[
"current_self_test_operation"] += {
570 jref[
"current_self_test_completion_percent"] = self_test_log.
current_completion & 0x7f;
575 for (
unsigned i = 0; i < 20; i++) {
579 if (!op || res == 0xf)
583 const char * t;
char buf2[32];
585 case 0x1: t =
"Short";
break;
586 case 0x2: t =
"Extended";
break;
587 case 0xe: t =
"Vendor specific";
break;
588 default: snprintf(buf2,
sizeof(buf2),
"Unknown (0x%x)", op);
593 case 0x0:
s =
"Completed without error";
break;
594 case 0x1:
s =
"Aborted: Self-test command";
break;
595 case 0x2:
s =
"Aborted: Controller Reset";
break;
596 case 0x3:
s =
"Aborted: Namespace removed";
break;
597 case 0x4:
s =
"Aborted: Format NVM command";
break;
598 case 0x5:
s =
"Fatal or unknown test error";
break;
599 case 0x6:
s =
"Completed: unknown failed segment";
break;
600 case 0x7:
s =
"Completed: failed segments";
break;
601 case 0x8:
s =
"Aborted: unknown reason";
break;
602 case 0x9:
s =
"Aborted: sanitize operation";
break;
603 default: snprintf(buf,
sizeof(buf),
"Unknown result (0x%x)", res);
610 {
"self_test_code", { {
"value", op }, {
"string", t } } },
611 {
"self_test_result", { {
"value", res }, {
"string",
s } } },
612 {
"power_on_hours", poh }
615 char sg[8] =
"-", ns[16] =
"-", lb[32] =
"-", st[8] =
"-", sc[8] =
"-";
617 snprintf(sg,
sizeof(sg),
"%d", r.
segment);
620 if (r.
valid & 0x01) {
622 ns[0] =
'*', ns[1] = 0;
624 snprintf(ns,
sizeof(ns),
"%u", r.
nsid);
628 if (r.
valid & 0x02) {
630 snprintf(lb,
sizeof(lb),
"%" PRIu64, lba);
633 if (r.
valid & 0x04) {
637 if (r.
valid & 0x08) {
643 jout(
"Num Test_Description Status Power_on_Hours Failing_LBA NSID Seg SCT Code\n");
644 jout(
"%2u %-17s %-33s %9" PRIu64
" %12s %5s %3s %3s %4s\n", i, t,
s, poh, lb, ns, sg, st, sc);
648 jout(
"No Self-tests Logged\n");
658 pout(
"NVMe device successfully opened\n\n"
659 "Use 'smartctl -a' (or '-x') to print SMART (and more) information\n\n");
669 jerr(
"Read NVMe Identify Controller failed: %s\n", device->
get_errmsg());
675 pout(
"=== START OF INFORMATION SECTION ===\n");
676 nvme_id_ns id_ns; memset(&id_ns, 0,
sizeof(id_ns));
681 if (id_ctrl.
nn == 1) {
705 pout(
"=== START OF SMART DATA SECTION ===\n");
711 unsigned smart_log_nsid = ((id_ctrl.
lpa & 0x01) ? device->
get_nsid()
716 jerr(
"Read NVMe SMART/Health Information (NSID 0x%x) failed: %s\n\n", smart_log_nsid,
733 bool lpo_sup = !!(id_ctrl.
lpa & 0x04);
737 unsigned max_entries = id_ctrl.
elpe + 1;
739 if (want_entries > max_entries)
740 want_entries = max_entries;
747 jerr(
"Read %u entries from Error Information Log failed: %s\n\n",
751 if (read_entries < want_entries)
752 jerr(
"Read Error Information Log failed, %u entries missing: %s\n",
753 want_entries - read_entries, device->
get_errmsg());
759 bool self_test_sup = !!(id_ctrl.
oacs & 0x0010);
762 int self_test_completion = -1;
765 pout(
"Self-tests not supported\n\n");
802 jerr(
"Read NVMe Log 0x%02x (NSID 0x%x) failed: %s\n\n", options.
log_page,
nsid,
806 if (read_bytes <
size)
807 jerr(
"Read NVMe Log 0x%02x failed, 0x%x bytes missing: %s\n",
810 pout(
"NVMe Log 0x%02x (NSID 0x%x, 0x%04x bytes)\n", options.
log_page,
nsid, read_bytes);
818 if (!self_test_abort && self_test_completion >= 0) {
819 pout(
"Can't start self-test without aborting current test (%2d%% completed)\n"
820 "Use smartctl -X to abort test\n", self_test_completion);
825 unsigned self_test_nsid = device->
get_nsid();
827 jerr(
"NVMe Self-test cmd with type=0x%x, nsid=0x%x failed: %s\n\n",
832 if (!self_test_abort)
833 pout(
"Self-test has begun (NSID 0x%x)\n"
834 "Use smartctl -X to abort test\n", self_test_nsid);
836 pout(
"Self-test aborted! (NSID 0x%x)\n", self_test_nsid);
bool dont_print_serial_number
Reference to a JSON element.
void set_unsafe_uint128(uint64_t value_hi, uint64_t value_lo)
void set_unsafe_le128(const void *pvalue)
void set_unsafe_uint64(uint64_t value)
unsigned get_nsid() const
Get namespace id.
const char * get_errmsg() const
Get last error message.
bool nvme_read_self_test_log(nvme_device *device, uint32_t nsid, smartmontools::nvme_self_test_log &self_test_log)
bool nvme_read_id_ns(nvme_device *device, unsigned nsid, nvme_id_ns &id_ns)
bool nvme_read_id_ctrl(nvme_device *device, nvme_id_ctrl &id_ctrl)
unsigned char nvme_debugmode
bool nvme_self_test(nvme_device *device, uint8_t stc, uint32_t nsid)
unsigned nvme_read_log_page(nvme_device *device, unsigned nsid, unsigned char lid, void *data, unsigned size, bool lpo_sup, unsigned offset)
unsigned nvme_read_error_log(nvme_device *device, nvme_error_log_page *error_log, unsigned num_entries, bool lpo_sup)
bool nvme_read_smart_log(nvme_device *device, uint32_t nsid, nvme_smart_log &smart_log)
const char * nvme_status_to_info_str(char *buf, size_t bufsize, uint16_t status)
constexpr uint32_t nvme_broadcast_nsid
static void print_self_test_log(const nvme_self_test_log &self_test_log, unsigned nsid)
static void print_drive_info(const nvme_id_ctrl &id_ctrl, const nvme_id_ns &id_ns, unsigned nsid, bool show_all)
static const char * le128_to_str(char(&str)[64], uint64_t hi, uint64_t lo, unsigned bytes_per_unit)
static void print_smart_log(const nvme_smart_log &smart_log, const nvme_id_ctrl &id_ctrl, unsigned nsid, bool show_all)
static void print_drive_capabilities(const nvme_id_ctrl &id_ctrl, const nvme_id_ns &id_ns, unsigned nsid, bool show_all)
int nvmePrintMain(nvme_device *device, const nvme_print_options &options)
static const char * format_power(char(&str)[16], unsigned power, unsigned scale)
static void lbacap_to_js(const json::ref &jref, uint64_t lba_cnt, int lba_bits)
static const char * kelvin_to_str(char(&str)[64], int k)
static void print_critical_warning(unsigned char w)
static void print_error_log(const nvme_error_log_page *error_log, unsigned read_entries, unsigned max_entries)
static const char * lbacap_to_str(char(&str)[64], uint64_t lba_cnt, int lba_bits)
static bool le128_is_non_zero(const unsigned char(&val)[16])
const char * nvmeprint_cvsid
#define NVMEPRINT_H_CVSID
void dStrHex(const uint8_t *up, int len, int no_ascii)
static uint64_t sg_get_unaligned_le64(const void *p)
static uint64_t sg_get_unaligned_be(int num_bytes, const void *p)
static uint16_t sg_get_unaligned_le16(const void *p)
static uint64_t sg_get_unaligned_le(int num_bytes, const void *p)
void jout_startup_datetime(const char *prefix)
void void void void jerr(const char *fmt,...) __attribute_format_printf(1
void jout(const char *fmt,...) __attribute_format_printf(1
void pout(const char *fmt,...)
unsigned char smart_selftest_type
unsigned error_log_entries
unsigned short status_field
unsigned short parm_error_location
const char * format_char_array(char *str, int strsize, const char *chr, int chrsize)
const char * format_capacity(char *str, int strsize, uint64_t val, const char *decimal_point)
const char * format_with_thousands_sep(char *str, int strsize, uint64_t val, const char *thousands_sep)
bool nonempty(const void *data, int size)
const char * uint128_hilo_to_str(char *str, int strsize, uint64_t value_hi, uint64_t value_lo)
int uint128_to_str_precision_bits()