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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.