From ed15ad23a4b605a2c91f3425b22be82de4119ac3 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Thu, 26 Feb 2026 12:53:55 +0100 Subject: [PATCH] Report more NVME SMART attributes --- cmdline/portable.h | 29 +++++++++++++++++++++++++---- cmdline/support.c | 25 ++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/cmdline/portable.h b/cmdline/portable.h index 9a7e1cdd..95c3d78c 100644 --- a/cmdline/portable.h +++ b/cmdline/portable.h @@ -434,7 +434,10 @@ int randomize(void* ptr, size_t size); #define SMART_LOAD_CYCLE_COUNT 193 #define SMART_TEMPERATURE_CELSIUS 194 -#define SMART_FLAGS 256 /**< Flags returned by smartctl. */ +/** + * Flags returned by smartctl via exit code + */ +#define SMART_FLAGS 256 /* * Counts command, transport, or controller-level errors reported by the device. @@ -467,6 +470,22 @@ int randomize(void* ptr, size_t size); */ #define SMART_COUNT 260 +/** + * NVME custom SMART attributes + * + * The numbers are just arbitrary to put them in unused space + */ +#define SMART_NVME_CRITICAL_WARNING 100 +#define SMART_NVME_AVAILABLE_SPARE 101 +#define SMART_NVME_DATA_UNITS_READ 102 +#define SMART_NVME_DATA_UNITS_WRITTEN 103 +#define SMART_NVME_HOST_READ_COMMANDS 104 +#define SMART_NVME_HOST_WRITE_COMMANDS 105 +#define SMART_NVME_CONTROLLER_BUSY_TIME 106 +#define SMART_NVME_UNSAFE_SHUTDOWNS 107 +#define SMART_NVME_WARNING_COMP_TEMPERATURE_TIME 108 +#define SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME 109 + /** * Info attributes. */ @@ -478,8 +497,10 @@ int randomize(void* ptr, size_t size); */ #define INFO_COUNT 2 - -#define SMART_IGNORE_MAX 4 /**< Max number of ignored smart attributes */ +/** + * Max number of ignored smart attributes + */ +#define SMART_IGNORE_MAX 4 /** * Flags returned by smartctl. @@ -546,7 +567,7 @@ struct devinfo_struct { struct devinfo_struct* parent; /**< Pointer at the parent if any. */ struct devinfo_struct* split; /**< Pointer at first split if any. */ struct smart_attr smart[SMART_COUNT]; /**< All smart values. */ - uint64_t info[INFO_COUNT]; /**< Info attributes. */ + uint64_t info[INFO_COUNT]; /**< Informational attributes not related to SMART telemetry. */ uint64_t access_stat; /**< Access stat info. */ char serial[SMART_MAX]; /**< Serial number. */ char family[SMART_MAX]; /**< Family. */ diff --git a/cmdline/support.c b/cmdline/support.c index 47d809ed..6cfa7404 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -2130,7 +2130,30 @@ int smartctl_attribute(FILE* f, const char* file, const char* name, struct smart pathcpy(smart[SMART_POWER_ON_HOURS].name, sizeof(smart[SMART_POWER_ON_HOURS].name), "Power_On_Hours"); } else if (scomma(s, "Power Cycles:", &smart[SMART_POWER_CYCLE_COUNT].raw) == 1) { pathcpy(smart[SMART_POWER_CYCLE_COUNT].name, sizeof(smart[SMART_POWER_CYCLE_COUNT].name), "Power_Cycles"); - /* Attributes */ + /* map specific attributes to UNUSED SMART attr */ + } else if (sscanf(s, "Critical Warning: %" SCNx64, &smart[SMART_NVME_CRITICAL_WARNING].raw) == 1) { + pathcpy(smart[SMART_NVME_CRITICAL_WARNING].name, sizeof(smart[SMART_NVME_CRITICAL_WARNING].name), "Critical_Warning"); + } else if (sscanf(s, "Available Spare: %" SCNu64 "%%", &smart[SMART_NVME_AVAILABLE_SPARE].raw) == 1) { + pathcpy(smart[SMART_NVME_AVAILABLE_SPARE].name, sizeof(smart[SMART_NVME_AVAILABLE_SPARE].name), "Available_Spare"); + } else if (scomma(s, "Data Units Read:", &smart[SMART_NVME_DATA_UNITS_READ].raw) == 1) { + pathcpy(smart[SMART_NVME_DATA_UNITS_READ].name, sizeof(smart[SMART_NVME_DATA_UNITS_READ].name), "Data_Units_Read"); + } else if (scomma(s, "Data Units Written:", &smart[SMART_NVME_DATA_UNITS_WRITTEN].raw) == 1) { + pathcpy(smart[SMART_NVME_DATA_UNITS_WRITTEN].name, sizeof(smart[SMART_NVME_DATA_UNITS_WRITTEN].name), "Data_Units_Written"); + } else if (scomma(s, "Host Read Commands:", &smart[SMART_NVME_HOST_READ_COMMANDS].raw) == 1) { + pathcpy(smart[SMART_NVME_HOST_READ_COMMANDS].name, sizeof(smart[SMART_NVME_HOST_READ_COMMANDS].name), "Host_Read_Commands"); + } else if (scomma(s, "Host Write Commands:", &smart[SMART_NVME_HOST_WRITE_COMMANDS].raw) == 1) { + pathcpy(smart[SMART_NVME_HOST_WRITE_COMMANDS].name, sizeof(smart[SMART_NVME_HOST_WRITE_COMMANDS].name), "Host_Write_Commands"); + } else if (scomma(s, "Controller Busy Time:", &smart[SMART_NVME_CONTROLLER_BUSY_TIME].raw) == 1) { + pathcpy(smart[SMART_NVME_CONTROLLER_BUSY_TIME].name, sizeof(smart[SMART_NVME_CONTROLLER_BUSY_TIME].name), "Controller_Busy_Time"); + } else if (scomma(s, "Unsafe Shutdowns:", &smart[SMART_NVME_UNSAFE_SHUTDOWNS].raw) == 1) { + pathcpy(smart[SMART_NVME_UNSAFE_SHUTDOWNS].name, sizeof(smart[SMART_NVME_UNSAFE_SHUTDOWNS].name), "Unsafe_Shutdowns"); + /* smartctl doesn't print this with command, but %d */ + } else if (scomma(s, "Warning Comp. Temperature Time:", &smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].raw) == 1) { + pathcpy(smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].name, sizeof(smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].name), "Warning_Comp_Temperature_Time"); + /* smartctl doesn't print this with command, but %d */ + } else if (scomma(s, "Critical Comp. Temperature Time:", &smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].raw) == 1) { + pathcpy(smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].name, sizeof(smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].name), "Critical_Comp_Temperature_Time"); + /* ATA Attributes table */ } else if (smatch(s, "ID#") == 0) { inside = 1; } else if (inside) {