From: piknew <mparka@wp.pl>
To: pve-devel@lists.proxmox.com
Subject: [PATCH] fix #7551: storage: normalize wearout detection using smartctl fields and types
Date: Mon, 9 Nov 2026 11:56:34 +0100 [thread overview]
Message-ID: <93435eb2-7b1d-4a1c-a727-9b9ef1855acd@PKPC.local> (raw)
Some SSD models (such as Crucial MX500) report the SMART attribute 202
(Percent_Lifetime_Remain) where the VALUE field wraps around or acts
unreliably, but the RAW field accurately holds the wearout percentage.
Conversely, other drives like Samsung use attribute 173 (Wear_Leveling_Count)
as a remaining life indicator inside the VALUE field.
This patch refactors get_wear_leveling_info by introducing a flag mapping
for known registers. It ensures that wearout is computed natively as "used
percentage" based on the exact source (VALUE or RAW) and calculation type
(Lifetime_Used or Lifetime_Remaining). Additionally, it fixes the nvme
"Percentage Used" parser in get_smart_data to align with the normalized
wearout approach.
Signed-off-by: piknew <mparka@wp.pl>
---
src/PVE/Diskmanage.pm | 45 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 43 insertions(+), 2 deletions(-)
diff --git a/src/PVE/Diskmanage.pm b/src/PVE/Diskmanage.pm
index f519f10..4b5555e 100644
--- a/src/PVE/Diskmanage.pm
+++ b/src/PVE/Diskmanage.pm
@@ -172,7 +172,7 @@ sub get_smart_data {
if ($line =~
m/Percentage Used(?: endurance indicator)?:\s*(\d+(?:\.\d+)?)\%/i
) {
- $smartdata->{wearout} = 100 - $1;
+ $smartdata->{wearout} = $1;
}
} elsif ($line =~ m/SMART Disabled/) {
$smartdata->{health} = "SMART Disabled";
@@ -466,12 +466,53 @@ sub get_wear_leveling_info {
"Perc_Rated_Life_Used",
);
+ # The flags, which determines:
+ # TYPE: if value is treated as "wear level" (Lifetime_Used) or "remaining life" (Lifetime_Remaining, default)
+ # SOURCE: which field in smart data is sourcing the value: value (VALUE, default) or raw (RAW)
+ my $wearoutregisterflags = {
+ # This is usage for SAMSUNG MZ7LN256HAJQ-000H1 (VALUE and Lifetime_Remaining shall be utilized):
+ # 173 Wear_Leveling_Count 0x0032 094 094 005 Old_age Always - 72 (this is 6% of usage for SAMSUNG MZ7LN256HAJQ-000H1)
+ "Wear_Leveling_Count" => {
+ "TYPE" => "Lifetime_Remaining",
+ "SOURCE" => "VALUE"
+ },
+ # These are usages for CRUCIALs CT250MX500SSD1 and CT500MX500SSD1 (RAW and Lifetime_Used shall be utilized):
+ # 202 Percent_Lifetime_Remain 0x0030 053 053 001 Old_age Offline - 47 (this is 47% of usage for CRUCIAL CT250MX500SSD1)
+ # 202 Percent_Lifetime_Remain 0x0030 047 047 001 Old_age Offline - 53 (this is 53% of usage for CRUCIAL CT250MX500SSD1)
+ # 202 Percent_Lifetime_Remain 0x0030 224 224 001 Old_age Offline - 132 (this is 132% of usage for CRUCIAL CT500MX500SSD1)
+ # 202 Percent_Lifetime_Remain 0x0030 253 253 001 Old_age Offline - 103 (this is 103% of usage for CRUCIAL CT250MX500SSD1)
+ "Percent_Lifetime_Remain" => {
+ "TYPE" => "Lifetime_Used",
+ "SOURCE" => "RAW"
+ },
+ # if attribute is not defined then fallback into defaults is provided:
+ "default" => {
+ "TYPE" => "Lifetime_Remaining",
+ "SOURCE" => "VALUE"
+ }
+ };
+
# Search for S.M.A.R.T. attributes for known register
foreach my $register (@wearoutregisters) {
last if defined $wearout;
+ my $flags = $wearoutregisterflags->{"default"};
+ if (defined($wearoutregisterflags->{$register})) {
+ $flags = $wearoutregisterflags->{$register}
+ }
foreach my $attr (@$attributes) {
next if $attr->{name} !~ m/$register/;
- $wearout = $attr->{value};
+ if ($flags->{"SOURCE"} eq "VALUE") {
+ $wearout = $attr->{value};
+ } elsif ($flags->{"SOURCE"} eq "RAW") {
+ $wearout = $attr->{raw};
+ }
+ if (defined($wearout)) {
+ if ($flags->{"TYPE"} eq "Lifetime_Remaining") {
+ # it is assumed that "remaining life" will never get nagative value
+ # so, it is converted to wearout ("used")
+ $wearout = 100 - $wearout;
+ }
+ }
last;
}
}
--
2.43.0.windows.1
reply other threads:[~2026-11-09 10:56 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=93435eb2-7b1d-4a1c-a727-9b9ef1855acd@PKPC.local \
--to=mparka@wp.pl \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox