all lists on 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 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.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal