From a1754b558f51f69c7b9cfc126b33220e73d7470f Mon Sep 17 00:00:00 2001
From: chrfranke <chrfranke@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Sat, 31 Oct 2009 16:59:55 +0000
Subject: [PATCH] smartctl: Ignore normalized attribute value and threshold if
 'raw64' or 'hex64' format is selected.

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@2980 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 smartmontools/CHANGELOG    |  3 ++
 smartmontools/atacmds.cpp  | 62 +++++-------------------------
 smartmontools/atacmds.h    |  8 +---
 smartmontools/ataprint.cpp | 79 +++++++++++++++++++++++++++++---------
 4 files changed, 75 insertions(+), 77 deletions(-)

diff --git a/smartmontools/CHANGELOG b/smartmontools/CHANGELOG
index 11c221a7b..611f3d3f0 100644
--- a/smartmontools/CHANGELOG
+++ b/smartmontools/CHANGELOG
@@ -43,6 +43,9 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] smartctl: Ignore normalized attribute value and threshold
+       if 'raw64' or 'hex24' format is selected.
+
   [CF] knowndrives.cpp updates:
        - add OCZ-Vertex raw64 attributes
        - add OCZ-Agility
diff --git a/smartmontools/atacmds.cpp b/smartmontools/atacmds.cpp
index ff4703dec..ff2e2282a 100644
--- a/smartmontools/atacmds.cpp
+++ b/smartmontools/atacmds.cpp
@@ -280,15 +280,20 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs,
   }
   ata_attr_raw_format format = format_names[i].format;
 
+  // 64-bit formats use the normalized value bytes.
+  if (format == RAWFMT_RAW64 || format == RAWFMT_HEX64)
+    flags |= ATTRFLAG_NO_NORMVAL;
+
   if (!id) {
     // "N,format" -> set format for all entries
-    for (int j = 0; j < MAX_ATTRIBUTE_NUM; j++) {
-      if (defs[j].priority >= priority)
+    for (i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
+      if (defs[i].priority >= priority)
         continue;
       if (attrname[0])
-        defs[j].name = attrname;
-      defs[j].priority = priority;
-      defs[j].raw_format = format;
+        defs[i].name = attrname;
+      defs[i].priority = priority;
+      defs[i].raw_format = format;
+      defs[i].flags = flags;
     }
   }
   else if (defs[id].priority <= priority) {
@@ -1698,53 +1703,6 @@ int isSupportSelectiveSelfTest(const ata_smart_values * data)
    return data->offline_data_collection_capability & 0x40;
 }
 
-
-
-// Loop over all valid attributes.  If they are prefailure attributes
-// and are at or below the threshold value, then return the ID of the
-// first failing attribute found.  Return 0 if all prefailure
-// attributes are in bounds.  The spec says "Bit 0
-// -Pre-failure/advisory - If the value of this bit equals zero, an
-// attribute value less than or equal to its corresponding attribute
-// threshold indicates an advisory condition where the usage or age of
-// the device has exceeded its intended design life period. If the
-// value of this bit equals one, an atribute value less than or equal
-// to its corresponding attribute threshold indicates a pre-failure
-// condition where imminent loss of data is being predicted."
-
-
-// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
-// onlyfailed=1:  are any prefailure attributes <= threshold now
-int ataCheckSmart(const ata_smart_values * data,
-                  const ata_smart_thresholds_pvt * thresholds,
-                  int onlyfailed)
-{
-  // loop over all attributes
-  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++){
-
-    // pointers to disk's values and vendor's thresholds
-    const ata_smart_attribute * disk = data->vendor_attributes+i;
-    const ata_smart_threshold_entry * thre = thresholds->thres_entries+i;
- 
-    // consider only valid attributes
-    if (disk->id && thre->id){
-      int failednow,failedever;
-      
-      failednow =disk->current <= thre->threshold;
-      failedever=disk->worst   <= thre->threshold;
-      
-      if (!onlyfailed && failedever)
-        return disk->id;
-      
-      if (onlyfailed && failednow && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
-        return disk->id;      
-    }
-  }
-  return 0;
-}
-
-
-
 // This checks the n'th attribute in the attribute list, NOT the
 // attribute with id==n.  If the attribute does not exist, or the
 // attribute is > threshold, then returns zero.  If the attribute is
diff --git a/smartmontools/atacmds.h b/smartmontools/atacmds.h
index 791c01470..2fea4126c 100644
--- a/smartmontools/atacmds.h
+++ b/smartmontools/atacmds.h
@@ -656,7 +656,8 @@ enum ata_attr_raw_format
 
 // Attribute flags
 enum {
-  ATTRFLAG_INCREASING = 0x01 // Value not reset (for reallocated/pending counts)
+  ATTRFLAG_INCREASING = 0x01, // Value not reset (for reallocated/pending counts)
+  ATTRFLAG_NO_NORMVAL = 0x02  // Normalized value not valid
 };
 
 // Vendor attribute display defs for all attribute ids
@@ -761,11 +762,6 @@ int ataSmartSupport(const ata_identify_device * drive);
 // -1: can't tell if SMART is enabled -- try issuing ataDoesSmartWork command to see
 int ataIsSmartEnabled(const ata_identify_device * drive);
 
-/* Check SMART for Threshold failure */
-// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
-// onlyfailed=1:  are any prefailure attributes <= threshold now
-int ataCheckSmart(const ata_smart_values * data, const ata_smart_thresholds_pvt * thresholds, int onlyfailed);
-
 int ataSmartStatus2(ata_device * device);
 
 int isSmartErrorLogCapable(const ata_smart_values * data, const ata_identify_device * identity);
diff --git a/smartmontools/ataprint.cpp b/smartmontools/ataprint.cpp
index a9511a115..9935206c0 100644
--- a/smartmontools/ataprint.cpp
+++ b/smartmontools/ataprint.cpp
@@ -773,6 +773,34 @@ static void PrintSmartConveyanceSelfTestPollingTime(const ata_smart_values * dat
     pout("recommended polling time: \t        Not Supported.\n");
 }
 
+// Check SMART attribute table for Threshold failure
+// onlyfailed=0: are or were any age or prefailure attributes <= threshold
+// onlyfailed=1: are any prefailure attributes <= threshold now
+static int find_failed_attr(const ata_smart_values * data,
+                            const ata_smart_thresholds_pvt * thresholds,
+                            const ata_vendor_attr_defs & defs, int onlyfailed)
+{
+  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    const ata_smart_attribute * disk = data->vendor_attributes+i;
+    const ata_smart_threshold_entry * thre = thresholds->thres_entries+i;
+
+    // consider only valid attributes
+    if (!(disk->id && disk->id == thre->id && thre->threshold
+          && !(defs[disk->id].flags & ATTRFLAG_NO_NORMVAL)   ))
+      continue;
+
+    bool failednow  = (disk->current <= thre->threshold);
+    bool failedever = (disk->worst   <= thre->threshold);
+
+    if (!onlyfailed && failedever)
+      return disk->id;
+
+    if (onlyfailed && failednow && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
+      return disk->id;
+  }
+  return 0;
+}
+
 // onlyfailed=0 : print all attribute values
 // onlyfailed=1:  just ones that are currently failed and have prefailure bit set
 // onlyfailed=2:  ones that are failed, or have failed with or without prefailure bit set
@@ -785,22 +813,28 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
 
   // step through all vendor attributes
   for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
-    const char *status;
     const ata_smart_attribute * disk = data->vendor_attributes+i;
     const ata_smart_threshold_entry * thre = thresholds->thres_entries+i;
     
     // consider only valid attributes (allowing some screw-ups in the
     // thresholds page data to slip by)
     if (disk->id){
-      const char *type, *update;
+      // Normalized values (current,worth,threshold) valid ?
+      // (Some SSD disks uses these bytes to store raw value).
+      bool norm_values_ok = !(defs[disk->id].flags & ATTRFLAG_NO_NORMVAL);
 
       // Don't report a failed attribute if its threshold is 0.
       // ATA-3 (X3T13/2008D Revision 7b) declares 0x00 as the "always passing"
       // threshold (Later ATA versions declare all thresholds as "obsolete").
       // In practice, threshold value 0 is often used for usage attributes or
       // appears if the thresholds cannot be read.
-      bool failednow  = (thre->threshold > 0 && disk->current <= thre->threshold);
-      bool failedever = (thre->threshold > 0 && disk->worst   <= thre->threshold);
+      bool failednow, failedever;
+      if (norm_values_ok && thre->threshold) {
+        failednow  = (disk->current <= thre->threshold);
+        failedever = (disk->worst   <= thre->threshold);
+      }
+      else
+        failednow = failedever = false;
 
       // These break out of the loop if we are only printing certain entries...
       if (onlyfailed==1 && (!ATTRIBUTE_FLAGS_PREFAILURE(disk->flags) || !failednow))
@@ -820,6 +854,7 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
       }
       
       // is this Attribute currently failed, or has it ever failed?
+      const char * status;
       if (failednow)
         status="FAILING_NOW";
       else if (failedever)
@@ -828,22 +863,28 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
         status="    -";
 
       // printing line for each valid attribute
-      type=ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age";
-      update=ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline";
-
-      pout("%3d %-24s0x%04x   %.3d   %.3d   %.3d    %-10s%-9s%-12s%s\n",
-           disk->id, ata_get_smart_attr_name(disk->id, defs).c_str(),
-           (int)disk->flags, (int)disk->current, (int)disk->worst,
-           (int)thre->threshold, type, update, status,
-           ata_format_attr_raw_value(*disk, defs).c_str());
+      std::string attrname = ata_get_smart_attr_name(disk->id, defs);
+      std::string rawstr = ata_format_attr_raw_value(*disk, defs);
+      const char * type = ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age";
+      const char * update = ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline";
+
+      if (norm_values_ok)
+        pout("%3d %-24s0x%04x   %.3d   %.3d   %.3d    %-10s%-9s%-12s%s\n",
+             disk->id, attrname.c_str(), disk->flags,
+             disk->current, disk->worst, thre->threshold,
+             type, update, status, rawstr.c_str());
+      else
+        pout("%3d %-24s0x%04x   ---   ---   ---    %-10s%-9s%-12s%s\n",
+             disk->id, attrname.c_str(), disk->flags,
+             type, update, status, rawstr.c_str());
 
       // Print a warning if there is inconsistency here and
       // threshold info is not empty.
-      if (disk->id != thre->id && (thre->id || thre->threshold)) {
+      if (norm_values_ok && disk->id != thre->id && (thre->id || thre->threshold)) {
         pout("%3d %-24s<== Data Page      |  WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",
-             disk->id, ata_get_smart_attr_name(disk->id, defs).c_str());
+             disk->id, attrname.c_str());
         pout("%3d %-24s<== Threshold Page |  INCONSISTENT IDENTITIES IN THE DATA\n",
-             thre->id, ata_get_smart_attr_name(thre->id, defs).c_str());
+             thre->id, attrname.c_str());
       }
     }
   }
@@ -1948,7 +1989,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     case 0:
       // The case where the disk health is OK
       pout("SMART overall-health self-assessment test result: PASSED\n");
-      if (ataCheckSmart(&smartval, &smartthres,0)){
+      if (find_failed_attr(&smartval, &smartthres, options.attribute_defs, 0)){
         if (options.smart_vendor_attrib)
           pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
         else {
@@ -1968,7 +2009,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       pout("SMART overall-health self-assessment test result: FAILED!\n"
            "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
       PRINT_OFF(con);
-      if (ataCheckSmart(&smartval, &smartthres,1)){
+      if (find_failed_attr(&smartval, &smartthres, options.attribute_defs, 1)){
         returnval|=FAILATTR;
         if (options.smart_vendor_attrib)
           pout("See vendor-specific Attribute list for failed Attributes.\n\n");
@@ -1987,7 +2028,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     case -1:
     default:
       // The case where something went wrong with HDIO_DRIVE_TASK ioctl()
-      if (ataCheckSmart(&smartval, &smartthres,1)){
+      if (find_failed_attr(&smartval, &smartthres, options.attribute_defs, 1)){
         PRINT_ON(con);
         pout("SMART overall-health self-assessment test result: FAILED!\n"
              "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
@@ -2004,7 +2045,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       }
       else {
         pout("SMART overall-health self-assessment test result: PASSED\n");
-        if (ataCheckSmart(&smartval, &smartthres,0)){
+        if (find_failed_attr(&smartval, &smartthres, options.attribute_defs, 0)){
           if (options.smart_vendor_attrib)
             pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
           else {
-- 
GitLab