public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal