]> git.proxmox.com Git - pve-storage.git/blobdiff - PVE/Diskmanage.pm
fix #1135: refactor wear level parsing
[pve-storage.git] / PVE / Diskmanage.pm
index 36a5b5b7cbac26ea1255143dc902c2a755710a22..1befd9e89214955b81a805976de42881fe91922c 100644 (file)
@@ -70,7 +70,7 @@ sub disk_is_used {
 }
 
 sub get_smart_data {
-    my ($disk) = @_;
+    my ($disk, $healthonly) = @_;
 
     assert_blockdev($disk);
     my $smartdata = {};
@@ -81,8 +81,12 @@ sub get_smart_data {
     $disk =~ s/n\d+$//
         if $disk =~ m!^/dev/nvme\d+n\d+$!;
 
+    my $cmd = [$SMARTCTL, '-H'];
+    push @$cmd, '-A', '-f', 'brief' if !$healthonly;
+    push @$cmd, $disk;
+
     eval {
-       $returncode = run_command([$SMARTCTL, '-H', '-A', '-f', 'brief', $disk], noerr => 1, outfunc => sub{
+       $returncode = run_command($cmd, noerr => 1, outfunc => sub{
            my ($line) = @_;
 
 # ATA SMART attributes, e.g.:
@@ -115,6 +119,8 @@ sub get_smart_data {
            } elsif (defined($type) && $type eq 'text') {
                $smartdata->{text} = '' if !defined $smartdata->{text};
                $smartdata->{text} .= "$line\n";
+           } elsif ($line =~ m/SMART Disabled/) {
+               $smartdata->{health} = "SMART Disabled";
            }
        });
     };
@@ -132,30 +138,6 @@ sub get_smart_data {
     return $smartdata;
 }
 
-sub get_smart_health {
-    my ($disk) = @_;
-
-    return "NOT A DEVICE" if !assert_blockdev($disk, 1);
-
-    my $message;
-    $disk =~ s/n\d+$//
-        if $disk =~ m!^/dev/nvme\d+n\d+$!;
-
-    run_command([$SMARTCTL, '-H', $disk], noerr => 1, outfunc => sub {
-       my ($line) = @_;
-
-       if ($line =~ m/test result: (.*)$/) {
-           $message = $1;
-       } elsif ($line =~ m/open device: (.*) failed: (.*)$/) {
-           $message = "FAILED TO OPEN";
-       } elsif ($line =~ m/^SMART Disabled/) {
-           $message = "SMART DISABLED";
-       }
-    });
-
-    return $message;
-}
-
 sub get_zfs_devices {
     my $list = {};
 
@@ -302,6 +284,44 @@ sub get_sysdir_info {
     return $data;
 }
 
+sub get_wear_leveling_info {
+    my ($attributes, $model) = @_;
+
+    my $wearout;
+
+    my $vendormap = {
+       'kingston' => 231,
+       'samsung' => 177,
+       'intel' => 233,
+       'sandisk' => 233,
+       'default' => 233,
+    };
+
+    # find target attr id
+
+    my $attrid;
+
+    foreach my $vendor (keys $vendormap) {
+       if ($model =~ m/$vendor/i) {
+           $attrid = $vendormap->{$vendor};
+           # found the attribute
+           last;
+       }
+    }
+
+    if (!$attrid) {
+       $attrid = $vendormap->{default};
+    }
+
+    foreach my $attr (@$attributes) {
+       next if $attr->{id} != $attrid;
+       $wearout = $attr->{value};
+       last;
+    }
+
+    return $wearout;
+}
+
 sub get_disks {
     my ($disk, $nosmart) = @_;
     my $disklist = {};
@@ -384,28 +404,17 @@ sub get_disks {
        }
 
        my $health = 'UNKNOWN';
-       my $wearout;
+       my $wearout = 'N/A';
 
        if (!$nosmart) {
            eval {
+               my $smartdata = get_smart_data($devpath, ($type ne 'ssd'));
+               $health = $smartdata->{health} if $smartdata->{health};
+
                if ($type eq 'ssd') {
                    # if we have an ssd we try to get the wearout indicator
-                   $wearout = 'N/A';
-                   my $smartdata = get_smart_data($devpath);
-                   $health = $smartdata->{health};
-                   foreach my $attr (@{$smartdata->{attributes}}) {
-                       # ID 233 is media wearout indicator on intel and sandisk
-                       # ID 177 is media wearout indicator on samsung
-                       next if ($attr->{id} != 233 && $attr->{id} != 177);
-                       next if ($attr->{name} !~ m/wear/i);
-                       $wearout = $attr->{value};
-
-                       # prefer the 233 value
-                       last if ($attr->{id} == 233);
-                   }
-               } else {
-                   # else we just get the health
-                   $health = get_smart_health($devpath);
+                   my $wearval = get_wear_leveling_info($smartdata->{attributes}, $sysdata->{model});
+                   $wearout = $wearval if $wearval;
                }
            };
        }