public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [PATCH] fix #7551: storage: normalize wearout detection using smartctl fields and types
@ 2026-11-09 10:56 piknew
  0 siblings, 0 replies; only message in thread
From: piknew @ 2026-11-09 10:56 UTC (permalink / raw)
  To: pve-devel

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





^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-11-09 10:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-09 10:56 [PATCH] fix #7551: storage: normalize wearout detection using smartctl fields and types piknew

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal