mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Added wear level percentage to the SMART report
This commit is contained in:
parent
e99f43c8ca
commit
ede99cbe80
7 changed files with 91 additions and 12 deletions
1
HISTORY
1
HISTORY
|
|
@ -12,6 +12,7 @@ SnapRAID HISTORY
|
|||
* Added detection of the smartctl executable. Improves reliability when running
|
||||
under sudo or in environments where /sbin and /usr/sbin are not in PATH.
|
||||
* Avoided saving the content file when it was not strictly necessary.
|
||||
* Added wear level percentage to the SMART report.
|
||||
* More log tags for the status and probe commands [Tristan Teufel].
|
||||
* Added documentation of the tags used in the log file in the new
|
||||
snapraid_log manpage.
|
||||
|
|
|
|||
|
|
@ -569,6 +569,8 @@ static void state_smart_log(devinfo_t* devinfo, double afr)
|
|||
log_tag("attr:%s:%s:error_protocol:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_ERROR_PROTOCOL]);
|
||||
if (devinfo->smart[SMART_ERROR_MEDIUM] != SMART_UNASSIGNED)
|
||||
log_tag("attr:%s:%s:error_medium:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_ERROR_MEDIUM]);
|
||||
if (devinfo->smart[SMART_WEAR_LEVEL] != SMART_UNASSIGNED)
|
||||
log_tag("attr:%s:%s:wear_level:%" PRIu64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_WEAR_LEVEL]);
|
||||
if (devinfo->smart[SMART_FLAGS] != SMART_UNASSIGNED)
|
||||
log_tag("attr:%s:%s:flags:%" PRIu64 ":%" PRIx64 "\n", devinfo->file, devinfo->name, devinfo->smart[SMART_FLAGS], devinfo->smart[SMART_FLAGS]);
|
||||
|
||||
|
|
@ -619,12 +621,14 @@ static void state_smart(struct snapraid_state* state, unsigned n, tommy_list* lo
|
|||
printf(" Power");
|
||||
printf(" Error");
|
||||
printf(" FP");
|
||||
printf(" Wear");
|
||||
printf(" Size");
|
||||
printf("\n");
|
||||
printf(" C");
|
||||
printf(" OnDays");
|
||||
printf(" Count");
|
||||
printf(" ");
|
||||
printf("Level");
|
||||
printf(" TB");
|
||||
printf(" "); printl("Serial", serial_pad);
|
||||
printf(" "); printl("Device", device_pad);
|
||||
|
|
@ -712,6 +716,12 @@ static void state_smart(struct snapraid_state* state, unsigned n, tommy_list* lo
|
|||
}
|
||||
}
|
||||
|
||||
if (devinfo->smart[SMART_WEAR_LEVEL] != SMART_UNASSIGNED) {
|
||||
printf(" %3" PRIu64 "%%", devinfo->smart[SMART_WEAR_LEVEL]);
|
||||
} else {
|
||||
printf(" -");
|
||||
}
|
||||
|
||||
if (devinfo->info[INFO_SIZE] != SMART_UNASSIGNED)
|
||||
printf(" %4.1f", devinfo->info[INFO_SIZE] / 1E12);
|
||||
else
|
||||
|
|
@ -783,9 +793,9 @@ static void state_smart(struct snapraid_state* state, unsigned n, tommy_list* lo
|
|||
|
||||
/* |<##################################################################72>|####80>| */
|
||||
printf("These values are the probabilities that in the next year you'll have a\n");
|
||||
printf("sequence of failures that the parity WON'T be able to recover, assuming\n");
|
||||
printf("that you regularly scrub, and in case repair, the array in the specified\n");
|
||||
printf("time.\n");
|
||||
printf("sequence of failures from which the parity won't be able to recover,\n");
|
||||
printf("assuming that you regularly scrub and repair the array (as needed)\n");
|
||||
printf("within the specified time.\n");
|
||||
|
||||
bail:
|
||||
if (make_it_fail) {
|
||||
|
|
|
|||
|
|
@ -451,10 +451,19 @@ int randomize(void* ptr, size_t size);
|
|||
*/
|
||||
#define SMART_ERROR_MEDIUM 258
|
||||
|
||||
/**
|
||||
* Unified Wear Level Metric (0-100)
|
||||
*
|
||||
* Represents the remaining life of an SSD.
|
||||
* - 0: Brand new drive (0% wear).
|
||||
* - 100: Drive has reached or exceeded its manufacturer-rated design life.
|
||||
*/
|
||||
#define SMART_WEAR_LEVEL 259
|
||||
|
||||
/**
|
||||
* SMART attributes count.
|
||||
*/
|
||||
#define SMART_COUNT 259
|
||||
#define SMART_COUNT 260
|
||||
|
||||
/**
|
||||
* Info attributes.
|
||||
|
|
|
|||
|
|
@ -1730,6 +1730,7 @@ int smartctl_attribute(FILE* f, const char* file, const char* name, uint64_t* sm
|
|||
char buf[256];
|
||||
unsigned id;
|
||||
uint64_t raw;
|
||||
uint64_t normalized;
|
||||
char* s;
|
||||
|
||||
s = fgets(buf, sizeof(buf), f);
|
||||
|
|
@ -1778,24 +1779,25 @@ int smartctl_attribute(FILE* f, const char* file, const char* name, uint64_t* sm
|
|||
} else if (sscanf(s, "Serial Number: %63s", serial) == 1) {
|
||||
} else if (smatch(s, "ID#") == 0) {
|
||||
inside = 1;
|
||||
} else if (smatch(s, "No Errors Logged") == 0) {
|
||||
} else if (smatch(s, "No Errors Logged") == 0) { /* ATA */
|
||||
smart[SMART_ERROR_PROTOCOL] = 0;
|
||||
} else if (sscanf(s, "ATA Error Count: %" SCNu64, &raw) == 1) {
|
||||
} else if (sscanf(s, "ATA Error Count: %" SCNu64, &raw) == 1) { /* ATA */
|
||||
smart[SMART_ERROR_PROTOCOL] = raw;
|
||||
} else if (sscanf(s, "Media and Data Integrity Errors: %" SCNu64, &raw) == 1) {
|
||||
} else if (sscanf(s, "Media and Data Integrity Errors: %" SCNu64, &raw) == 1) { /* NVME */
|
||||
smart[SMART_ERROR_MEDIUM] = raw;
|
||||
} else if (sscanf(s, "Error Information Log Entries: %" SCNu64, &raw) == 1) {
|
||||
} else if (sscanf(s, "Error Information Log Entries: %" SCNu64, &raw) == 1) { /* NVME */
|
||||
smart[SMART_ERROR_PROTOCOL] = raw;
|
||||
} else if (sscanf(s, "Medium error count: %" SCNu64, &raw) == 1) {
|
||||
smart[SMART_ERROR_MEDIUM] = raw;
|
||||
} else if (sscanf(s, "Non-medium error count: %" SCNu64, &raw) == 1) {
|
||||
} else if (sscanf(s, "Non-medium error count: %" SCNu64, &raw) == 1) { /* SCSI */
|
||||
smart[SMART_ERROR_PROTOCOL] = raw;
|
||||
} else if (sscanf(s, "Percentage Used: %" SCNu64 "%%", &raw) == 1) { /* NVME */
|
||||
if (raw <= 100)
|
||||
smart[SMART_WEAR_LEVEL] = raw;
|
||||
} else if (inside) {
|
||||
if (sscanf(s, "%u %*s %*s %*s %*s %*s %*s %*s %*s %" SCNu64, &id, &raw) != 2) {
|
||||
/* LCOV_EXCL_START */
|
||||
if (sscanf(s, "%u %*s %*s %" SCNu64 " %*s %*s %*s %*s %*s %" SCNu64, &id, &normalized, &raw) != 3) {
|
||||
log_fatal("Invalid smartctl line '%s'.\n", s);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (id >= 256) {
|
||||
|
|
@ -1806,6 +1808,22 @@ int smartctl_attribute(FILE* f, const char* file, const char* name, uint64_t* sm
|
|||
}
|
||||
|
||||
smart[id] = raw;
|
||||
|
||||
/*
|
||||
* Map normalized health percentage to our unified wear level for SSDs
|
||||
* 177: Wear_Leveling_Count (Samsung/Crucial)
|
||||
* 231: SSD_Life_Left (Kingston/WD)
|
||||
* 233: Media_Wearout_Indicator (Intel)
|
||||
*/
|
||||
if (normalized <= 100) {
|
||||
switch (id) {
|
||||
case 177 :
|
||||
case 233 :
|
||||
case 231 :
|
||||
smart[SMART_WEAR_LEVEL] = 100 - normalized;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1640,6 +1640,23 @@ The total error count (uint).
|
|||
.PD
|
||||
.RE
|
||||
.TP
|
||||
.B attr:<device_file>:<disk_name>[/<split_index>]:wear_level:<percentage>
|
||||
The wear level of the SSD/NVME expressed as a percentage, where a
|
||||
value of 0 represents a brand new drive with zero wear and
|
||||
a value of 100 indicates the drive has reached or exceeded its
|
||||
manufacturer\-rated design life. This metric is provided as a
|
||||
normalized unsigned integer, offering a consistent health
|
||||
indicator across various SSD/NVME vendors and interface types
|
||||
by mapping diverse hardware counters into a single
|
||||
standardized scale.
|
||||
.RS 4
|
||||
.PD 0
|
||||
.HP 4
|
||||
.I <percentage>
|
||||
Wear level percentage (uint).
|
||||
.PD
|
||||
.RE
|
||||
.TP
|
||||
.B attr:<device_file>:<disk_name>[/<split_index>]:rotationrate:<rate>
|
||||
Logs the disk\'s rotation rate.
|
||||
.RS 4
|
||||
|
|
|
|||
|
|
@ -1037,6 +1037,18 @@ Command Smart And Probe Tags
|
|||
|
||||
<error_count> - The total error count (uint).
|
||||
|
||||
=attr:<device_file>:<disk_name>[/<split_index>]:wear_level:<percentage>
|
||||
The wear level of the SSD/NVME expressed as a percentage, where a
|
||||
value of 0 represents a brand new drive with zero wear and
|
||||
a value of 100 indicates the drive has reached or exceeded its
|
||||
manufacturer-rated design life. This metric is provided as a
|
||||
normalized unsigned integer, offering a consistent health
|
||||
indicator across various SSD/NVME vendors and interface types
|
||||
by mapping diverse hardware counters into a single
|
||||
standardized scale.
|
||||
|
||||
<percentage> - Wear level percentage (uint).
|
||||
|
||||
=attr:<device_file>:<disk_name>[/<split_index>]:rotationrate:<rate>
|
||||
Logs the disk's rotation rate.
|
||||
|
||||
|
|
|
|||
|
|
@ -1083,6 +1083,18 @@ Tags logging specific disk identification and attribute values.
|
|||
|
||||
<error_count> - The total error count (uint).
|
||||
|
||||
attr:<device_file>:<disk_name>[/<split_index>]:wear_level:<percentage>
|
||||
The wear level of the SSD/NVME expressed as a percentage, where a
|
||||
value of 0 represents a brand new drive with zero wear and
|
||||
a value of 100 indicates the drive has reached or exceeded its
|
||||
manufacturer-rated design life. This metric is provided as a
|
||||
normalized unsigned integer, offering a consistent health
|
||||
indicator across various SSD/NVME vendors and interface types
|
||||
by mapping diverse hardware counters into a single
|
||||
standardized scale.
|
||||
|
||||
<percentage> - Wear level percentage (uint).
|
||||
|
||||
attr:<device_file>:<disk_name>[/<split_index>]:rotationrate:<rate>
|
||||
Logs the disk's rotation rate.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue