snapraid/cmdline/device.c
Andrea Mazzoleni 951e4e5c98 Add the ATA error count in the SMART report.
And other minor changes.
2015-01-15 22:47:21 +01:00

286 lines
6.8 KiB
C
Raw Blame History

/*
* Copyright (C) 2015 Andrea Mazzoleni
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "portable.h"
#include "support.h"
#include "state.h"
/**
* Annual Failure Rate data points from Backblaze.
*
* From:
* https://www.backblaze.com/blog-smart-stats-2014-8.html
*/
struct afr_point {
uint64_t value; /**< Value of the SMART raw attribute. */
double afr; /**< Annual Failure Rate at this value. */
};
static struct afr_point AFR_5[] = {
{ 0, 0 },
{ 1, 0.027432608477803388 },
{ 4, 0.07501976284584981 },
{ 16, 0.23589260654405794 },
{ 70, 0.36193219378600433 },
{ 260, 0.5676621428968173 },
{ 1100, 1.5028253400346423 },
{ 4500, 2.0659987547404763 },
{ 17000, 1.7755385684503124 },
{ 0, 0 }
};
static struct afr_point AFR_187[] = {
{ 0, 0 },
{ 1, 0.33877621175661743 },
{ 3, 0.5014425058387142 },
{ 11, 0.5346094598348444 },
{ 20, 0.8428063943161636 },
{ 35, 1.4429071005017484 },
{ 65, 1.6190935390549661 },
{ 0, 0 }
};
static struct afr_point AFR_188[] = {
{ 0, 0 },
{ 1, 0.10044174089362015 },
{ 13000000000ULL, 0.334030592234279 },
{ 26000000000ULL, 0.36724705400842445 },
{ 0, 0 }
};
static struct afr_point AFR_193[] = {
{ 0, 0 },
{ 1300, 0.024800489215129725 },
{ 5500, 0.05859661417772557 },
{ 21000, 0.19566577603409208 },
{ 90000, 0.2673688205712117 },
{ 0, 0 }
};
static struct afr_point AFR_197[] = {
{ 0, 0 },
{ 1, 0.34196613799103254 },
{ 2, 0.6823772508117681 },
{ 16, 0.9564879341127684 },
{ 40, 1.6519989942167461 },
{ 100, 2.5137741046831956 },
{ 250, 3.3203378817413904 },
{ 0, 0 }
};
static struct afr_point AFR_198[] = {
{ 0, 0 },
{ 1, 0.8135764944275583 },
{ 2, 1.1173469387755102 },
{ 4, 1.3558692421991083 },
{ 10, 1.7464114832535886 },
{ 12, 2.6449275362318843 },
{ 0, 0 }
};
/**
* Computes the estimated AFR from a set of data points.
*/
static double smart_afr_value(struct afr_point* tab, uint64_t value)
{
unsigned i;
double delta_afr;
double delta_value;
/* if first point */
if (value == 0)
return 0;
i = 1;
while (tab[i].value != 0 && tab[i].value < value)
++i;
/* if last point */
if (tab[i].value == 0)
return tab[i-1].afr;
/* if exact value */
if (tab[i].value == value)
return tab[i].afr;
delta_afr = tab[i].afr - tab[i-1].afr;
delta_value = tab[i].value - tab[i-1].value;
/* linear interpolation between the two points */
return tab[i-1].afr + (value - tab[i-1].value) * delta_afr / delta_value;
}
/**
* Computes the estimated AFR of a set of SMART attributes.
*/
static double smart_afr(uint64_t* smart)
{
double afr = 0;
if (smart[5] != SMART_UNASSIGNED)
afr += smart_afr_value(AFR_5, smart[5]);
if (smart[187] != SMART_UNASSIGNED)
afr += smart_afr_value(AFR_187, smart[187]);
if (smart[188] != SMART_UNASSIGNED)
afr += smart_afr_value(AFR_188, smart[188]);
if (smart[193] != SMART_UNASSIGNED)
afr += smart_afr_value(AFR_193, smart[193]);
if (smart[197] != SMART_UNASSIGNED)
afr += smart_afr_value(AFR_197, smart[197]);
if (smart[198] != SMART_UNASSIGNED)
afr += smart_afr_value(AFR_198, smart[198]);
return afr;
}
static void state_smart(tommy_list* low)
{
tommy_node* i;
printf("SnapRAID SMART report:\n");
printf("\n");
printf(" Temp");
printf(" Power");
printf(" Error");
printf("\n");
printf(" C<>");
printf(" OnDays");
printf(" Count");
printf(" AFR");
printf(" Size");
printf(" Serial/Name");
printf("\n");
printf(" --------------------------------------------------------------------------\n");
for (i = tommy_list_head(low); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
double afr;
if (devinfo->smart[194] != SMART_UNASSIGNED)
printf("%7" PRIu64, devinfo->smart[194]);
else if (devinfo->smart[190] != SMART_UNASSIGNED)
printf("%7" PRIu64, devinfo->smart[190]);
else
printf(" -");
if (devinfo->smart[9] != SMART_UNASSIGNED)
printf("%7" PRIu64, devinfo->smart[9] / 24);
else
printf(" -");
if (devinfo->smart[SMART_ERROR] != SMART_UNASSIGNED)
printf("%6" PRIu64, devinfo->smart[SMART_ERROR]);
else
printf(" -");
afr = smart_afr(devinfo->smart);
printf("%5.0f%%", afr * 100);
if (devinfo->smart[SMART_SIZE] != SMART_UNASSIGNED)
printf(" %2.1fT", devinfo->smart[SMART_SIZE] / 1E12);
else
printf(" -");
printf(" %s", devinfo->smart_serial);
printf("/%s", devinfo->name);
printf("\n");
}
printf("\n");
}
void state_device(struct snapraid_state* state, int operation)
{
tommy_node* i;
unsigned j;
tommy_list high;
tommy_list low;
switch (operation) {
case DEVICE_UP : printf("Spinup...\n"); break;
case DEVICE_DOWN : printf("Spindown...\n"); break;
}
tommy_list_init(&high);
tommy_list_init(&low);
/* for all disks */
for (i = state->disklist; i != 0; i = i->next) {
struct snapraid_disk* disk = i->data;
devinfo_t* entry;
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = disk->device;
pathcpy(entry->name, sizeof(entry->name), disk->name);
pathcpy(entry->mount, sizeof(entry->mount), disk->dir);
tommy_list_insert_tail(&high, &entry->node, entry);
}
/* for all parities */
for (j = 0; j < state->level; ++j) {
devinfo_t* entry;
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = state->parity[j].device;
pathcpy(entry->name, sizeof(entry->name), lev_config_name(j));
pathcpy(entry->mount, sizeof(entry->mount), state->parity[j].path);
pathcut(entry->mount); /* remove the parity file */
tommy_list_insert_tail(&high, &entry->node, entry);
}
if (devquery(&high, &low, operation) != 0) {
switch (operation) {
case DEVICE_UP : fprintf(stderr, "Spinup"); break;
case DEVICE_DOWN : fprintf(stderr, "Spindown"); break;
case DEVICE_LIST : fprintf(stderr, "List"); break;
case DEVICE_SMART : fprintf(stderr, "SMART"); break;
}
fprintf(stderr, " unsupported in this platform.\n");
}
#ifndef _WIN32
if (operation == DEVICE_LIST) {
for (i = tommy_list_head(&low); i != 0; i = i->next) {
devinfo_t* devinfo = i->data;
devinfo_t* parent = devinfo->parent;
printf("%u:%u\t%s\t%u:%u\t%s\t%s\n", major(devinfo->device), minor(devinfo->device), devinfo->file, major(parent->device), minor(parent->device), parent->file, parent->name);
}
}
#endif
if (operation == DEVICE_SMART) {
state_smart(&low);
}
tommy_list_foreach(&high, free);
tommy_list_foreach(&low, free);
}