diff -up smartmontools-7.1/dev_interface.cpp.r5471 smartmontools-7.1/dev_interface.cpp --- smartmontools-7.1/dev_interface.cpp.r5471 2019-11-24 19:19:24.000000000 +0100 +++ smartmontools-7.1/dev_interface.cpp 2023-11-22 14:07:37.647756091 +0100 @@ -15,6 +15,7 @@ #include "dev_tunnelled.h" #include "atacmds.h" // ATA_SMART_CMD/STATUS #include "scsicmds.h" // scsi_cmnd_io +#include "nvmecmds.h" // nvme_status_*() #include "utility.h" #include @@ -235,12 +236,11 @@ bool scsi_device::scsi_pass_through_and_ bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */) { - if (!status) - throw std::logic_error("nvme_device: set_nvme_err() called with status=0"); - out.status = status; out.status_valid = true; - return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status); + char buf[64]; + return set_err(nvme_status_to_errno(status), "%s%s (0x%03x)", (msg ? msg : ""), + nvme_status_to_info_str(buf, status), status); } diff -up smartmontools-7.1/nvmecmds.cpp.r5471 smartmontools-7.1/nvmecmds.cpp --- smartmontools-7.1/nvmecmds.cpp.r5471 2023-11-22 14:07:37.646756079 +0100 +++ smartmontools-7.1/nvmecmds.cpp 2023-11-22 14:07:37.648756102 +0100 @@ -258,3 +258,221 @@ bool nvme_read_smart_log(nvme_device * d return true; } + +// Return flagged error message for NVMe status SCT/SC fields or nullptr if unknown. +// If message starts with '-', the status indicates an invalid command (EINVAL). +static const char * nvme_status_to_flagged_str(uint16_t status) +{ + // Section 3.3.3.2.1 of NVM Express Base Specification Revision 2.0c, October 4, 2022 + uint8_t sc = (uint8_t)status; + switch ((status >> 8) & 0x7) { + case 0x0: // Generic Command Status + if (sc < 0x80) switch (sc) { + case 0x00: return "Successful Completion"; + case 0x01: return "-Invalid Command Opcode"; + case 0x02: return "-Invalid Field in Command"; + case 0x03: return "Command ID Conflict"; + case 0x04: return "Data Transfer Error"; + case 0x05: return "Commands Aborted due to Power Loss Notification"; + case 0x06: return "Internal Error"; + case 0x07: return "Command Abort Requested"; + case 0x08: return "Command Aborted due to SQ Deletion"; + case 0x09: return "Command Aborted due to Failed Fused Command"; + case 0x0a: return "Command Aborted due to Missing Fused Command"; + case 0x0b: return "-Invalid Namespace or Format"; + case 0x0c: return "Command Sequence Error"; + case 0x0d: return "-Invalid SGL Segment Descriptor"; + case 0x0e: return "-Invalid Number of SGL Descriptors"; + case 0x0f: return "-Data SGL Length Invalid"; + case 0x10: return "-Metadata SGL Length Invalid"; + case 0x11: return "-SGL Descriptor Type Invalid"; + case 0x12: return "-Invalid Use of Controller Memory Buffer"; + case 0x13: return "-PRP Offset Invalid"; + case 0x14: return "Atomic Write Unit Exceeded"; + case 0x15: return "Operation Denied"; + case 0x16: return "-SGL Offset Invalid"; + case 0x18: return "Host Identifier Inconsistent Format"; + case 0x19: return "Keep Alive Timer Expired"; + case 0x1a: return "-Keep Alive Timeout Invalid"; + case 0x1b: return "Command Aborted due to Preempt and Abort"; + case 0x1c: return "Sanitize Failed"; + case 0x1d: return "Sanitize In Progress"; + case 0x1e: return "SGL Data Block Granularity Invalid"; + case 0x1f: return "Command Not Supported for Queue in CMB"; + case 0x20: return "Namespace is Write Protected"; + case 0x21: return "Command Interrupted"; + case 0x22: return "Transient Transport Error"; + case 0x23: return "Command Prohibited by Command and Feature Lockdown"; + case 0x24: return "Admin Command Media Not Ready"; + // 0x25-0x7f: Reserved + } + else switch (sc) { + // 0x80-0xbf: I/O Command Set Specific + case 0x80: return "LBA Out of Range"; + case 0x81: return "Capacity Exceeded"; + case 0x82: return "Namespace Not Ready"; + case 0x83: return "Reservation Conflict"; + case 0x84: return "Format In Progress"; + case 0x85: return "-Invalid Value Size"; + case 0x86: return "-Invalid Key Size"; + case 0x87: return "KV Key Does Not Exist"; + case 0x88: return "Unrecovered Error"; + case 0x89: return "Key Exists"; + // 0x90-0xbf: Reserved + // 0xc0-0xff: Vendor Specific + } + break; + + case 0x1: // Command Specific Status + if (sc < 0x80) switch (sc) { + case 0x00: return "-Completion Queue Invalid"; + case 0x01: return "-Invalid Queue Identifier"; + case 0x02: return "-Invalid Queue Size"; + case 0x03: return "Abort Command Limit Exceeded"; + case 0x04: return "Abort Command Is Missing"; + case 0x05: return "Asynchronous Event Request Limit Exceeded"; + case 0x06: return "-Invalid Firmware Slot"; + case 0x07: return "-Invalid Firmware Image"; + case 0x08: return "-Invalid Interrupt Vector"; + case 0x09: return "-Invalid Log Page"; + case 0x0a: return "-Invalid Format"; + case 0x0b: return "Firmware Activation Requires Conventional Reset"; + case 0x0c: return "-Invalid Queue Deletion"; + case 0x0d: return "Feature Identifier Not Saveable"; + case 0x0e: return "Feature Not Changeable"; + case 0x0f: return "Feature Not Namespace Specific"; + case 0x10: return "Firmware Activation Requires NVM Subsystem Reset"; + case 0x11: return "Firmware Activation Requires Controller Level Reset"; + case 0x12: return "Firmware Activation Requires Maximum Time Violation"; + case 0x13: return "Firmware Activation Prohibited"; + case 0x14: return "Overlapping Range"; + case 0x15: return "Namespace Insufficient Capacity"; + case 0x16: return "-Namespace Identifier Unavailable"; + case 0x18: return "Namespace Already Attached"; + case 0x19: return "Namespace Is Private"; + case 0x1a: return "Namespace Not Attached"; + case 0x1b: return "Thin Provisioning Not Supported"; + case 0x1c: return "-Controller List Invalid"; + case 0x1d: return "Device Self-test In Progress"; + case 0x1e: return "Boot Partition Write Prohibited"; + case 0x1f: return "Invalid Controller Identifier"; + case 0x20: return "-Invalid Secondary Controller State"; + case 0x21: return "-Invalid Number of Controller Resources"; + case 0x22: return "-Invalid Resource Identifier"; + case 0x23: return "Sanitize Prohibited While Persistent Memory Region is Enabled"; + case 0x24: return "-ANA Group Identifier Invalid"; + case 0x25: return "ANA Attach Failed"; + case 0x26: return "Insufficient Capacity"; + case 0x27: return "Namespace Attachment Limit Exceeded"; + case 0x28: return "Prohibition of Command Execution Not Supported"; + case 0x29: return "I/O Command Set Not Supported"; + case 0x2a: return "I/O Command Set Not Enabled"; + case 0x2b: return "I/O Command Set Combination Rejected"; + case 0x2c: return "-Invalid I/O Command Set"; + case 0x2d: return "-Identifier Unavailable"; + // 0x2e-0x6f: Reserved + // 0x70-0x7f: Directive Specific + } + else if (sc < 0xb8) switch (sc) { + // 0x80-0xbf: I/O Command Set Specific (overlap with Fabrics Command Set) + case 0x80: return "-Conflicting Attributes"; + case 0x81: return "-Invalid Protection Information"; + case 0x82: return "Attempted Write to Read Only Range"; + case 0x83: return "Command Size Limit Exceeded"; + // 0x84-0xb7: Reserved + } + else switch (sc) { + case 0xb8: return "Zoned Boundary Error"; + case 0xb9: return "Zone Is Full"; + case 0xba: return "Zone Is Read Only"; + case 0xbb: return "Zone Is Offline"; + case 0xbc: return "Zone Invalid Write"; + case 0xbd: return "Too Many Active Zones"; + case 0xbe: return "Too Many Open Zones"; + case 0xbf: return "Invalid Zone State Transition"; + // 0xc0-0xff: Vendor Specific + } + break; + + case 0x2: // Media and Data Integrity Errors + switch (sc) { + // 0x00-0x7f: Reserved + case 0x80: return "Write Fault"; + case 0x81: return "Unrecovered Read Error"; + case 0x82: return "End-to-end Guard Check Error"; + case 0x83: return "End-to-end Application Tag Check Error"; + case 0x84: return "End-to-end Reference Tag Check Error"; + case 0x85: return "Compare Failure"; + case 0x86: return "Access Denied"; + case 0x87: return "Deallocated or Unwritten Logical Block"; + case 0x88: return "End-to-End Storage Tag Check Error"; + // 0x89-0xbf: Reserved + // 0xc0-0xff: Vendor Specific + } + break; + + case 0x3: // Path Related Status + switch (sc) { + case 0x00: return "Internal Path Error"; + case 0x01: return "Asymmetric Access Persistent Loss"; + case 0x02: return "Asymmetric Access Inaccessible"; + case 0x03: return "Asymmetric Access Transition"; + // 0x04-0x5f: Reserved + // 0x60-0x6f: Controller Detected Pathing Errors + case 0x60: return "Controller Pathing Error"; + // 0x61-0x6f: Reserved + // 0x70-0x7f: Host Detected Pathing Errors + case 0x70: return "Host Pathing Error"; + case 0x71: return "Command Aborted By Host"; + // 0x72-0x7f: Reserved + // 0x80-0xbf: I/O Command Set Specific + // 0xc0-0xff: Vendor Specific + } + break; + + // 0x4-0x6: Reserved + // 0x7: Vendor Specific + } + return nullptr; +} + +// Return errno for NVMe status SCT/SC fields: 0, EINVAL or EIO. +int nvme_status_to_errno(uint16_t status) +{ + if (!nvme_status_is_error(status)) + return 0; + const char * s = nvme_status_to_flagged_str(status); + if (s && *s == '-') + return EINVAL; + return EIO; +} + +// Return error message for NVMe status SCT/SC fields or nullptr if unknown. +const char * nvme_status_to_str(uint16_t status) +{ + const char * s = nvme_status_to_flagged_str(status); + return (s && *s == '-' ? s + 1 : s); +} + +// Return error message for NVMe status SCT/SC fields or explanatory message if unknown. +const char * nvme_status_to_info_str(char * buf, size_t bufsize, uint16_t status) +{ + const char * s = nvme_status_to_str(status); + if (s) + return s; + + uint8_t sct = (status >> 8) & 0x7, sc = (uint8_t)status; + const char * pfx = (sc >= 0xc0 ? "Vendor Specific " : "Unknown "); + switch (sct) { + case 0x0: s = "Generic Command Status"; break; + case 0x1: s = "Command Specific Status"; break; + case 0x2: s = "Media and Data Integrity Error"; break; + case 0x3: s = "Path Related Status"; break; + case 0x7: s = "Vendor Specific Status"; pfx = ""; break; + } + if (s) + snprintf(buf, bufsize, "%s%s 0x%02x", pfx, s, sc); + else + snprintf(buf, bufsize, "Unknown Status 0x%x/0x%02x", sct, sc); + return buf; +} diff -up smartmontools-7.1/nvmecmds.h.r5471 smartmontools-7.1/nvmecmds.h --- smartmontools-7.1/nvmecmds.h.r5471 2023-11-22 14:07:37.646756079 +0100 +++ smartmontools-7.1/nvmecmds.h 2023-11-22 14:09:29.911084240 +0100 @@ -18,6 +18,8 @@ #include "static_assert.h" +#include +#include #include // The code below was originally imported from include file from @@ -246,4 +248,22 @@ unsigned nvme_read_error_log(nvme_device // Read NVMe SMART/Health Information log. bool nvme_read_smart_log(nvme_device * device, smartmontools::nvme_smart_log & smart_log); +// Return true if NVMe status indicates an error. +constexpr bool nvme_status_is_error(uint16_t status) + { return !!(status & 0x07ff); } + +// Return errno for NVMe status SCT/SC fields: 0, EINVAL or EIO. +int nvme_status_to_errno(uint16_t status); + +// Return error message for NVMe status SCT/SC fields or nullptr if unknown. +const char * nvme_status_to_str(uint16_t status); + +// Return error message for NVMe status SCT/SC fields or explanatory message if unknown. +const char * nvme_status_to_info_str(char * buf, size_t bufsize, uint16_t status); + +// Version of above for fixed size buffers. +template +inline const char * nvme_status_to_info_str(char (& buf)[SIZE], unsigned status) + { return nvme_status_to_info_str(buf, SIZE, status); } + #endif // NVMECMDS_H diff -up smartmontools-7.1/nvmeprint.cpp.r5471 smartmontools-7.1/nvmeprint.cpp --- smartmontools-7.1/nvmeprint.cpp.r5471 2023-11-22 14:07:37.648756102 +0100 +++ smartmontools-7.1/nvmeprint.cpp 2023-11-22 14:11:35.899574762 +0100 @@ -420,7 +420,7 @@ static void print_error_log(const nvme_e continue; if (cnt == 1) - pout("Num ErrCount SQId CmdId Status PELoc LBA NSID VS\n"); + pout("Num ErrCount SQId CmdId Status PELoc LBA NSID VS Message\n"); char sq[16] = "-", cm[16] = "-", st[16] = "-", pe[16] = "-"; char lb[32] = "-", ns[16] = "-", vs[8] = "-"; @@ -439,8 +439,10 @@ static void print_error_log(const nvme_e if (e.vs != 0x00) snprintf(vs, sizeof(vs), "0x%02x", e.vs); - pout("%3u %10" PRIu64 " %5s %7s %7s %6s %12s %5s %5s\n", - i, e.error_count, sq, cm, st, pe, lb, ns, vs); + char buf[64]; + pout("%3u %10" PRIu64 " %5s %7s %7s %6s %12s %5s %5s %s\n", + i, e.error_count, sq, cm, st, pe, lb, ns, vs, + nvme_status_to_info_str(buf, e.status_field >> 1)); } if (!cnt)