"Fossies" - the Fresh Open Source Software Archive

Member "stress-ng-0.13.05/core-smart.c" (11 Oct 2021, 13523 Bytes) of package /linux/privat/stress-ng-0.13.05.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "core-smart.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.13.04_vs_0.13.05.

    1 /*
    2  * Copyright (C) 2021 Canonical, Ltd.
    3  *
    4  * This program is free software; you can redistribute it and/or
    5  * modify it under the terms of the GNU General Public License
    6  * as published by the Free Software Foundation; either version 2
    7  * of the License, or (at your option) any later version.
    8  *
    9  * This program is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with this program; if not, write to the Free Software
   16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
   17  *
   18  */
   19 #include "stress-ng.h"
   20 
   21 
   22 #define DEVS_MAX            (256)
   23 
   24 #define SENSE_BUF_SZ        (0x20)
   25 #define BUF_SZ          (0x200)
   26 
   27 /*
   28  *  See https://www.t10.org/ftp/t10/document.04/04-262r8.pdf
   29  */
   30 #define CBD_OPERATION_CODE  (0xa1) /* Operation code */
   31 #define CBD_PROTOCOL_DMA    (0x06) /* Protocol DMA */
   32 #define CBD_T_LENGTH        (0x02) /* Tx len in SECTOR_COUNT field */
   33 #define CBD_BYT_BLOK        (0x01) /* Tx len in byte blocks */
   34 #define CBD_T_DIR       (0x01) /* Tx direction, device -> client */
   35 #define CBD_CK_COND     (0x00) /* Check condition, disabled */
   36 #define CBD_OFF_LINE        (0x00) /* offline time, 0 seconds */
   37 #define CBD_FEATURES        (0xd0) /* feature: read smart data */
   38 #define CBD_SECTOR_COUNT    (0x01) /* 1 sector to read */
   39 #define CBD_LBA_LOW     (0x00) /* LBA: 0:7 N/A */
   40 #define CBD_LBA_MID     (0x4f) /* LBA: 23:8 magic: 0xc24f */
   41 #define CBD_LBA_HIGH        (0xc2)
   42 #define CBD_DEVICE      (0x00) /* all zero */
   43 #define CBD_COMMAND     (0xb0) /* command: read smart log */
   44 #define CBD_RESVERVED       (0x00) /* N/A */
   45 #define CBD_CONTROL     (0x00)
   46 
   47 #define ATTR_FLAG_WARRANTY  (0x01)
   48 #define ATTR_FLAG_OFFLINE   (0x02)
   49 #define ATTR_FLAG_PERFORMANCE   (0x04)
   50 #define ATTR_FLAG_ERROR_RATE    (0x08)
   51 #define ATTR_FLAG_EVENT_COUNT   (0x10)
   52 #define ATTR_FLAG_SELF_PRESERV  (0x20)
   53 
   54 /* SMART log raw data value */
   55 typedef struct __attribute__ ((packed)) {
   56     uint8_t     attr_id;
   57     uint16_t    attr_flags;
   58     uint8_t     current_value;
   59     uint8_t     worst_value;
   60     uint32_t    data;
   61     uint16_t    attr_data;
   62     uint8_t     threshold;
   63 } stress_smart_raw_value_t;
   64 
   65 typedef struct {
   66     size_t      count;
   67     size_t      size;
   68     stress_smart_raw_value_t values[];
   69 } stress_smart_data_t;
   70 
   71 typedef struct stress_smart_dev_t {
   72     char *dev_name;
   73     stress_smart_data_t *data_begin;
   74     stress_smart_data_t *data_end;
   75     struct stress_smart_dev_t *next;
   76 } stress_smart_dev_t;
   77 
   78 typedef struct {
   79     stress_smart_dev_t *dev;
   80 } stress_smart_devs_t;
   81 
   82 static stress_smart_devs_t smart_devs;
   83 
   84 #if defined(HAVE_SCSI_SG_H) &&  \
   85     defined(HAVE_SCSI_SCSI_IOCTL_H)
   86 
   87 #define HAVE_SMART  (1)
   88 
   89 /*
   90  *  S.M.A.R.T. ID Descriptions, see:
   91  *  https://en.wikipedia.org/wiki/S.M.A.R.T.#Known_ATA_S.M.A.R.T._attributes
   92  */
   93 static const char *id_str[256] = {
   94     [0x01] = "Read Error Rate",
   95     [0x02] = "Throughput Performance",
   96     [0x03] = "Spin-Up Time",
   97     [0x04] = "Start/Stop Count",
   98     [0x05] = "Reallocated Sectors Count",
   99     [0x06] = "Read Channel Margin",
  100     [0x07] = "Seek Error Rate",
  101     [0x08] = "Seek Time Performance",
  102     [0x09] = "Power-On Hours",
  103     [0x0a] = "Spin Retry Count",
  104     [0x0b] = "Recalibration Retries",
  105     [0x0c] = "Power Cycle Count",
  106     [0x0d] = "Soft Read Error Rate",
  107     [0x16] = "Current Helium Level",
  108     [0xaa] = "Available Reserved Space",
  109     [0xab] = "SSD Program Fail Count",
  110     [0xac] = "SSD Erase Fail Count",
  111     [0xad] = "SSD Wear Leveling Count",
  112     [0xae] = "Unexpected Power Loss Count",
  113     [0xaf] = "Power Loss Protection Failure",
  114     [0xb0] = "Erase Fail Count",
  115     [0xb1] = "Wear Range Delta",
  116     [0xb2] = "Used Reserved Block Count",
  117     [0xb3] = "Used Reserved Block Count Total",
  118     [0xb4] = "Unused Reserved Block Count Total",
  119     [0xb5] = "Program Fail Count Total",
  120     [0xb6] = "Erase Fail Count",
  121     [0xb7] = "SATA Downshift Error Count",
  122     [0xb8] = "End-to-End error",
  123     [0xb9] = "Head Stability",
  124     [0xba] = "Induced Op-Vibration Detection",
  125     [0xbb] = "Reported Uncorrectable Errors",
  126     [0xbc] = "Command Timeout",
  127     [0xbd] = "High Fly Writes",
  128     [0xbe] = "Temperature Difference",
  129     [0xbf] = "G-sense Error Rate",
  130     [0xc0] = "Power-off Retract Count",
  131     [0xc1] = "Load Cycle Count",
  132     [0xc2] = "Temperature",
  133     [0xc3] = "Hardware ECC Recovered",
  134     [0xc4] = "Reallocation Event Count",
  135     [0xc5] = "Current Pending Sector Count",
  136     [0xc6] = "(Offline) Uncorrectable Sector Count",
  137     [0xc7] = "UltraDMA CRC Error Count",
  138     [0xc8] = "Multi-Zone Error Rate",
  139     [0xc9] = "Soft Read Error Rate",
  140     [0xca] = "Data Address Mark errors",
  141     [0xcb] = "Run Out Cancel",
  142     [0xcc] = "Soft ECC Correction",
  143     [0xcd] = "Thermal Asperity Rate",
  144     [0xce] = "Flying Height",
  145     [0xcf] = "Spin High Current",
  146     [0xd0] = "Spin Buzz",
  147     [0xd1] = "Offline Seek Performance",
  148     [0xd2] = "Vibration During Write",
  149     [0xd3] = "Vibration During Write",
  150     [0xd4] = "Shock During Write",
  151     [0xdc] = "Disk Shift",
  152     [0xdd] = "G-Sense Error Rate",
  153     [0xde] = "Loaded Hours",
  154     [0xdf] = "Load/Unload Retry Count",
  155     [0xe0] = "Load Friction",
  156     [0xe1] = "Load/Unload Cycle Count",
  157     [0xe2] = "Load 'In'-time",
  158     [0xe3] = "Torque Amplification Count",
  159     [0xe4] = "Power-Off Retract Cycle",
  160     [0xe6] = "GMR Head Amplitude",
  161     [0xe7] = "Life Left / Temperature",
  162     [0xe8] = "Endurance Remaining",
  163     [0xe9] = "Media Wearout Indicator",
  164     [0xea] = "Average erase count",
  165     [0xeb] = "Good Block Count",
  166     [0xf0] = "Head Flying Hours",
  167     [0xf1] = "Total LBAs Written",
  168     [0xf2] = "Total LBAs Read",
  169     [0xf3] = "Total LBAs Written Expanded",
  170     [0xf4] = "Total LBAs Read Expanded",
  171     [0xf9] = "NAND Writes (1GiB)",
  172     [0xfa] = "Read Error Retry Rate",
  173     [0xfb] = "Minimum Spares Remaining",
  174     [0xfc] = "Newly Added Bad Flash Block",
  175     [0xfe] = "Free Fall Protection",
  176 };
  177 
  178 /*
  179  *  S.M.A.R.T command block
  180  */
  181 static uint8_t cdb[] = {
  182     CBD_OPERATION_CODE,
  183     CBD_PROTOCOL_DMA << 1,
  184     ((CBD_T_LENGTH << 0) |
  185      (CBD_BYT_BLOK << 1) |
  186      (CBD_T_DIR << 3) |
  187      (CBD_CK_COND << 5) |
  188      (CBD_OFF_LINE << 6)),
  189     CBD_FEATURES,
  190     CBD_SECTOR_COUNT,
  191     CBD_LBA_LOW,
  192     CBD_LBA_MID,
  193     CBD_LBA_HIGH,
  194     CBD_DEVICE,
  195     CBD_COMMAND,
  196     CBD_RESVERVED,
  197     CBD_CONTROL
  198 };
  199 
  200 /*
  201  *  stress_smart_data_free()
  202  *  free smart data
  203  */
  204 static void stress_smart_data_free(stress_smart_data_t **data)
  205 {
  206     if (*data) {
  207         (*data)->count = 0;
  208         (*data)->size = 0;
  209         free(*data);
  210         *data = NULL;
  211     }
  212 }
  213 
  214 /*
  215  *  stress_smart_data_read()
  216  *  read smart data from a device
  217  */
  218 static stress_smart_data_t *stress_smart_data_read(const char *path)
  219 {
  220     int fd;
  221     uint8_t buf[BUF_SZ], sbuf[SENSE_BUF_SZ];
  222     sg_io_hdr_t sg_io_hdr;
  223     const stress_smart_raw_value_t *rv_start = (const stress_smart_raw_value_t *)(buf + 2);
  224     const stress_smart_raw_value_t *rv_end = (const stress_smart_raw_value_t *)(buf + sizeof(buf));
  225     const stress_smart_raw_value_t *rv;
  226     stress_smart_data_t *data;
  227     size_t i, size, values_size;
  228 
  229     if (!path)
  230         return NULL;
  231 
  232     fd = open(path, O_RDONLY);
  233     if (fd < 0)
  234         return NULL;
  235 
  236     (void)memset(&sg_io_hdr, 0, sizeof(sg_io_hdr));
  237     sg_io_hdr.interface_id = 'S';
  238     sg_io_hdr.cmd_len = sizeof(cdb);
  239     sg_io_hdr.mx_sb_len = sizeof(sbuf);
  240     sg_io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
  241     sg_io_hdr.dxfer_len = sizeof(buf);
  242     sg_io_hdr.dxferp = buf;
  243     sg_io_hdr.cmdp = cdb;
  244     sg_io_hdr.sbp = sbuf;
  245     sg_io_hdr.timeout = 35000;
  246     (void)memset(buf, 0, sizeof(buf));
  247 
  248     if (ioctl(fd, SG_IO, &sg_io_hdr) < 0) {
  249         (void)close(fd);
  250         return NULL;
  251     }
  252     (void)close(fd);
  253 
  254     for (i = 0, rv = rv_start; (rv < rv_end) && (rv->attr_id); rv++, i++)
  255         ;
  256 
  257     values_size = i * sizeof(stress_smart_raw_value_t);
  258     size = sizeof(stress_smart_data_t) + values_size;
  259     data = malloc(size);
  260     if (!data)
  261         return NULL;
  262 
  263     (void)memcpy(data->values, rv_start, values_size);
  264     data->size = size;
  265     data->count = i;
  266     return data;
  267 }
  268 
  269 /*
  270  *  stress_smart_data_diff_count()
  271  *  count smart data changes between begin and end runs
  272  */
  273 static size_t stress_smart_data_diff_count(stress_smart_dev_t *dev)
  274 {
  275     size_t i, n;
  276     stress_smart_data_t *begin, *end;
  277 
  278     begin = dev->data_begin;
  279     end = dev->data_end;
  280 
  281     if (!begin || !begin->count)
  282         return 0;
  283     if (!end || !end->count)
  284         return 0;
  285 
  286     for (n = 0, i = 0; i < begin->count; i++) {
  287         const stress_smart_raw_value_t *rv1 = &begin->values[i];
  288         const uint8_t attr_id = rv1->attr_id;
  289         size_t j;
  290 
  291         for (j = 0; j < end->count; j++) {
  292             const stress_smart_raw_value_t *rv2 = &end->values[j];
  293 
  294             if (attr_id == rv2->attr_id) {
  295                 if (rv2->data - rv1->data)
  296                     n++;    /* a value changed, count it */
  297                 break;
  298             }
  299         }
  300     }
  301     return n;
  302 }
  303 
  304 /*
  305  *  stress_smart_data_diff()
  306  *  print device and smart attributes that changed
  307  */
  308 static void stress_smart_data_diff(stress_smart_dev_t *dev)
  309 {
  310     size_t i, n;
  311     stress_smart_data_t *begin, *end;
  312 
  313     begin = dev->data_begin;
  314     end = dev->data_end;
  315 
  316     if (!begin || !end)
  317         return;
  318     if (!begin->count || !end->count)
  319         return;
  320 
  321     for (n = 0, i = 0; i < begin->count; i++) {
  322         const stress_smart_raw_value_t *rv1 = &begin->values[i];
  323         const uint8_t attr_id = rv1->attr_id;
  324         size_t j;
  325 
  326         for (j = 0; j < end->count; j++) {
  327             const stress_smart_raw_value_t *rv2 = &end->values[j];
  328 
  329             if (attr_id == rv2->attr_id) {
  330                 int32_t delta = rv2->data - rv1->data;
  331 
  332                 if (delta) {
  333                     const char *dev_name = (strncmp(dev->dev_name, "/dev/", 5) == 0) ?
  334                                 dev->dev_name + 5 : dev->dev_name;
  335                     pr_inf("%-10.10s %2.2x %-30.30s %11" PRIu32 " %11" PRId32 "\n",
  336                         dev_name, attr_id,
  337                         id_str[attr_id] ? id_str[attr_id] : "?",
  338                         rv2->data, delta);
  339                     n++;
  340                 }
  341                 break;
  342             }
  343         }
  344     }
  345 }
  346 
  347 #else
  348 
  349 /*
  350  *  stress_smart_data_diff_count()
  351  *  count smart data changes between begin and end runs, no-op
  352  */
  353 static size_t stress_smart_data_diff_count(stress_smart_dev_t *dev)
  354 {
  355     (void)dev;
  356 
  357     return 0;
  358 }
  359 
  360 /*
  361  *  stress_smart_data_free()
  362  *  free smart data, no-op
  363  */
  364 static void stress_smart_data_free(stress_smart_data_t **data)
  365 {
  366     (void)data;
  367 }
  368 
  369 /*
  370  *  stress_smart_data_read()
  371  *  read smart data from a device, no-op
  372  */
  373 static stress_smart_data_t *stress_smart_data_read(const char *path)
  374 
  375 {
  376     (void)path;
  377 
  378     return NULL;
  379 }
  380 
  381 /*
  382  *  stress_smart_data_diff()
  383  *  print device and smart attributes that changed, no-op
  384  */
  385 static void stress_smart_data_diff(stress_smart_dev_t *dev)
  386 {
  387     (void)dev;
  388 }
  389 
  390 #endif
  391 
  392 #if defined(HAVE_SMART)
  393 /*
  394  *  stress_smart_dev_filter()
  395  *  discard entries that don't look like device names
  396  */
  397 static int stress_smart_dev_filter(const struct dirent *d)
  398 {
  399     size_t len;
  400 
  401     if ((d->d_name[0] == '\0') || (d->d_name[0] == '.'))
  402         return 0;
  403     len = strlen(d->d_name);
  404     if (len < 1)        /* Also unlikely */
  405         return 0;
  406     if (isdigit((int)d->d_name[len - 1]))
  407         return 0;
  408 
  409     return 1;
  410 }
  411 
  412 static int stress_smart_dev_sort(const struct dirent **d1, const struct dirent **d2)
  413 {
  414     int cmp;
  415 
  416     cmp = strcmp((*d1)->d_name, (*d2)->d_name);
  417     if (cmp < 0)
  418         return -1;
  419     if (cmp > 1)
  420         return 1;
  421     return 0;
  422 }
  423 
  424 /*
  425  *  stress_smart_read_devs()
  426  *  scan across block devices and populate a linked list
  427  *  of all devices that can supply S.M.A.R.T. data
  428  */
  429 static void stress_smart_read_devs(void)
  430 {
  431     struct dirent **devs = NULL;
  432     size_t i, n;
  433 
  434     smart_devs.dev = NULL;
  435 
  436     n = (size_t)scandir("/dev", &devs, stress_smart_dev_filter, stress_smart_dev_sort);
  437     if (n < 1)
  438         return;
  439 
  440     for (i = 0; i < n; i++) {
  441         char path[PATH_MAX];
  442         struct stat buf;
  443         int ret;
  444         const struct dirent *d = devs[i];
  445 
  446         (void)snprintf(path, sizeof(path), "/dev/%s", d->d_name);
  447         ret = stat(path, &buf);
  448         if ((ret == 0) && (S_ISBLK(buf.st_mode))) {
  449             stress_smart_dev_t *dev;
  450             stress_smart_data_t *data;
  451 
  452             data = stress_smart_data_read(path);
  453             if (data) {
  454                 /* Allocate, silently ignore alloc failure */
  455                 dev = calloc(1, sizeof(*dev));
  456                 if (dev) {
  457                     dev->dev_name = strdup(path);
  458                     dev->data_begin = data;
  459                     dev->data_end = NULL;
  460                     dev->next = smart_devs.dev;
  461                     smart_devs.dev = dev;
  462                 } else {
  463                     stress_smart_data_free(&data);
  464                 }
  465             }
  466         }
  467         free(devs[i]);
  468     }
  469     free(devs);
  470 }
  471 #endif
  472 
  473 /*
  474  *  stress_smart_free_devs()
  475  *  free list of smart enabled device information
  476  */
  477 static void stress_smart_free_devs(void)
  478 {
  479     stress_smart_dev_t *dev = smart_devs.dev;
  480 
  481     while (dev) {
  482         stress_smart_dev_t *next = dev->next;
  483 
  484         free(dev->dev_name);
  485         stress_smart_data_free(&dev->data_begin);
  486         stress_smart_data_free(&dev->data_end);
  487         free(dev);
  488 
  489         dev = next;
  490     }
  491     smart_devs.dev = NULL;
  492 }
  493 
  494 /*
  495  *  stress_smart_start()
  496  *  fetch beginning smart data
  497  */
  498 void stress_smart_start(void)
  499 {
  500     if (g_opt_flags & OPT_FLAGS_SMART) {
  501 #if defined(HAVE_SMART)
  502         stress_smart_read_devs();
  503 #else
  504         pr_inf("note: --smart option not available for this system\n");
  505 #endif
  506     }
  507 }
  508 
  509 /*
  510  *  stress_smart_stop()
  511  *  fetch stop smart data and print any changes
  512  */
  513 void stress_smart_stop(void)
  514 {
  515     if (g_opt_flags & OPT_FLAGS_SMART) {
  516         stress_smart_dev_t *dev;
  517         size_t deltas;
  518         size_t devs;
  519 
  520         for (devs = 0, deltas = 0, dev = smart_devs.dev; dev; dev = dev->next) {
  521             dev->data_end = stress_smart_data_read(dev->dev_name);
  522             deltas += stress_smart_data_diff_count(dev);
  523             devs++;
  524         }
  525 
  526         if (deltas) {
  527             pr_inf("%-10.10s %2.2s %-30.30s %11.11s %11.11s\n",
  528                 "Device", "ID", "S.M.A.R.T. Attribute", "Value", "Change");
  529             for (dev = smart_devs.dev; dev; dev = dev->next) {
  530                 stress_smart_data_diff(dev);
  531             }
  532         } else {
  533             if (devs == 0) {
  534 #if defined(HAVE_SMART)
  535                 char *extra;
  536 
  537                 if (stress_check_capability(SHIM_CAP_IS_ROOT)) {
  538                     extra = "";
  539                 } else {
  540                     extra = " (try running as root)";
  541                 }
  542                 pr_inf("could not find any S.M.A.R.T. enabled devices%s\n", extra);
  543 #endif
  544             } else {
  545                 pr_inf("no S.M.A.R.T. data statistics changed on %zd device%s\n",
  546                     devs, devs > 1 ? "s" : "");
  547             }
  548         }
  549         stress_smart_free_devs();
  550     }
  551 }