From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id EC9851FF09B for ; Mon, 09 Nov 2026 11:56:51 +0100 (CET) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id F2A972161B; Mon, 09 Nov 2026 11:56:48 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=wp.pl; s=s20261001; t=1794221795; bh=dyUpiMRMbYrou+9eB44Qu3AUjRfSv8NrQvctGbyeN4Y=; h=From:Sender:To:Subject; b=bBWqz6YN3d5BV6QrSzb7rAI4r9Sjro/9h7YRX9+0wtnvXY5JYRvQeG87+awohxsWS BlYKqpjiLCiO2360syJcbelQ/g/9rNte1E5/IQi1+ZheMLwgacpf1XpJ2EsDwo+Q3w IGUfoEGQsjUJwGu6Jf4lMnGNEKm0EBPnlpZT+KVKnQ5REq+89ZOO4M3iGFWGsziGZj 0e8IpYUCGku6AFAMLDFtCWW3BN6tCo2pDU9zOWJXU66C9vBXSBLkWD7JPyPfSYykOv XtCDVp9iW/hKm+PSeZAMi2OKNXdAaD1nPmjzDQe9rUmW6ecuaqpMfnVbxglHjrIspT SD2ALpgZOU52Q== Date: Mon, 9 Nov 2026 11:56:34 +0100 From: piknew Sender: piknew To: pve-devel@lists.proxmox.com Subject: [PATCH] fix #7551: storage: normalize wearout detection using smartctl fields and types Message-ID: <93435eb2-7b1d-4a1c-a727-9b9ef1855acd@PKPC.local> X-Mailer: TortoiseGit MIME-Version: 1.0 Content-Type: text/plain Content-Transfer-Encoding: 8bit X-WP-MailID: bae605de68f1d6049c225ee5d9154e4e X-WP-AV: skaner antywirusowy Poczty Wirtualnej Polski X-WP-SPAM: NO 000000A [AcNk] X-SPAM-LEVEL: Spam detection results: 0 AWL -0.000 Adjusted score from AWL reputation of From: address DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy FREEMAIL_FROM 0.001 Sender email is commonly abused enduser mail provider RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust RCVD_IN_MSPIKE_H2 0.001 Average reputation (+2) SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: DCACLVDGOVWLYJCBLW2FMNAGAFXH6FDI X-Message-ID-Hash: DCACLVDGOVWLYJCBLW2FMNAGAFXH6FDI X-MailFrom: mparka@wp.pl X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: 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 --- 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