mirror of
https://github.com/amadvance/snapraid.git
synced 2026-10-26 10:51:54 -04:00
Add preliminary SMART support with a new "smart" command.
This commit is contained in:
parent
16b872e30c
commit
4a41adc807
7 changed files with 375 additions and 85 deletions
2
HISTORY
2
HISTORY
|
|
@ -3,6 +3,8 @@ SnapRAID HISTORY
|
|||
|
||||
8.0 2015/01 BETA
|
||||
================
|
||||
* Adds new "smart" command to print a SMART report of the array.
|
||||
At now it's available only in Linux.
|
||||
* Adds new "up" and "down" commands to spin up and down the disks of
|
||||
the array. Note that "down" is available only in Linux.
|
||||
* Allows "sync" and "scrub" to continue after the first
|
||||
|
|
|
|||
213
cmdline/device.c
213
cmdline/device.c
|
|
@ -20,18 +20,202 @@
|
|||
#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("\n");
|
||||
printf(" C°");
|
||||
printf(" OnDays");
|
||||
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(" -");
|
||||
|
||||
afr = smart_afr(devinfo->smart);
|
||||
|
||||
printf("%5.0f%%", afr * 100);
|
||||
|
||||
printf(" %2.1fT", devinfo->smart_size / 1E12);
|
||||
|
||||
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 list;
|
||||
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(&list);
|
||||
tommy_list_init(&high);
|
||||
tommy_list_init(&low);
|
||||
|
||||
/* for all disks */
|
||||
for (i = state->disklist; i != 0; i = i->next) {
|
||||
|
|
@ -44,7 +228,7 @@ void state_device(struct snapraid_state* state, int operation)
|
|||
pathcpy(entry->name, sizeof(entry->name), disk->name);
|
||||
pathcpy(entry->mount, sizeof(entry->mount), disk->dir);
|
||||
|
||||
tommy_list_insert_tail(&list, &entry->node, entry);
|
||||
tommy_list_insert_tail(&high, &entry->node, entry);
|
||||
}
|
||||
|
||||
/* for all parities */
|
||||
|
|
@ -58,18 +242,35 @@ void state_device(struct snapraid_state* state, int operation)
|
|||
pathcpy(entry->mount, sizeof(entry->mount), state->parity[j].path);
|
||||
pathcut(entry->mount); /* remove the parity file */
|
||||
|
||||
tommy_list_insert_tail(&list, &entry->node, entry);
|
||||
tommy_list_insert_tail(&high, &entry->node, entry);
|
||||
}
|
||||
|
||||
if (devquery(&list, operation) != 0) {
|
||||
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");
|
||||
}
|
||||
|
||||
tommy_list_foreach(&list, free);
|
||||
#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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1821,17 +1821,19 @@ static void* thread_spinup(void* arg)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int devquery(tommy_list* list, int operation)
|
||||
int devquery(tommy_list* high, tommy_list* low, int operation)
|
||||
{
|
||||
tommy_node* i;
|
||||
int fail = 0;
|
||||
|
||||
(void)low;
|
||||
|
||||
/* we support only thread_spinup */
|
||||
if (operation != DEVICE_UP)
|
||||
return -1;
|
||||
|
||||
/* starts all threads */
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
for (i = tommy_list_head(high); i != 0; i = i->next) {
|
||||
devinfo_t* devinfo = i->data;
|
||||
|
||||
if (pthread_create(&devinfo->thread, 0, thread_spinup, devinfo) != 0) {
|
||||
|
|
@ -1843,7 +1845,7 @@ int devquery(tommy_list* list, int operation)
|
|||
}
|
||||
|
||||
/* joins all threads */
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
for (i = tommy_list_head(high); i != 0; i = i->next) {
|
||||
devinfo_t* devinfo = i->data;
|
||||
void* retval;
|
||||
if (pthread_join(devinfo->thread, &retval) != 0) {
|
||||
|
|
|
|||
|
|
@ -303,13 +303,32 @@ int exit_failure;
|
|||
int randomize(void* ptr, size_t size);
|
||||
|
||||
/**
|
||||
* Disk entry.
|
||||
* SMART attributes.
|
||||
*/
|
||||
#define SMART_COUNT 256
|
||||
|
||||
/**
|
||||
* SMART max attribute length.
|
||||
*/
|
||||
#define SMART_MAX 64
|
||||
|
||||
/**
|
||||
* Value for unassigned SMART attribute.
|
||||
*/
|
||||
#define SMART_UNASSIGNED 0xFFFFFFFFFFFFFFFFULL
|
||||
|
||||
/**
|
||||
* Device info entry.
|
||||
*/
|
||||
struct devinfo_struct {
|
||||
uint64_t device; /**< Device ID. */
|
||||
char name[PATH_MAX]; /**< Name of the disk. */
|
||||
char mount[PATH_MAX]; /**< Mount point or other contained directory. */
|
||||
char file[PATH_MAX]; /**< File device. */
|
||||
struct devinfo_struct* parent; /**< Pointer at the parent if any. */
|
||||
uint64_t smart[SMART_COUNT]; /**< SMART raw attributes. */
|
||||
uint64_t smart_size; /**< SMART size. */
|
||||
char smart_serial[SMART_MAX]; /**< SMART serial number. */
|
||||
#if HAVE_PTHREAD_CREATE
|
||||
pthread_t thread;
|
||||
#endif
|
||||
|
|
@ -317,14 +336,18 @@ struct devinfo_struct {
|
|||
};
|
||||
typedef struct devinfo_struct devinfo_t;
|
||||
|
||||
#define DEVICE_DOWN 0
|
||||
#define DEVICE_UP 1
|
||||
#define DEVICE_LIST -1
|
||||
#define DEVICE_LIST 0
|
||||
#define DEVICE_DOWN 1
|
||||
#define DEVICE_UP 2
|
||||
#define DEVICE_SMART 3
|
||||
|
||||
/**
|
||||
* Spin all disks.
|
||||
* Query all the "high" level devices with the specified operation,
|
||||
* and produces a list of "low" level devices to operate on.
|
||||
*
|
||||
* The resulting "low" device list must be already initialized.
|
||||
*/
|
||||
int devquery(tommy_list* list, int operation);
|
||||
int devquery(tommy_list* high, tommy_list* low, int operation);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ void usage(void)
|
|||
{
|
||||
version();
|
||||
|
||||
printf("Usage: " PACKAGE " status|diff|sync|scrub|list|dup|up|down|pool|check|fix [options]\n");
|
||||
printf("Usage: " PACKAGE " status|diff|sync|scrub|list|dup|up|down|smart|pool|check|fix [options]\n");
|
||||
printf("\n");
|
||||
printf("Commands:\n");
|
||||
printf(" status Print the status of the array\n");
|
||||
|
|
@ -48,6 +48,7 @@ void usage(void)
|
|||
printf(" dup Find duplicate files\n");
|
||||
printf(" up Spinup the array\n");
|
||||
printf(" down Spindown the array\n");
|
||||
printf(" smart SMART attributes of the array\n");
|
||||
printf(" pool Create or update the virtual view of the array\n");
|
||||
printf(" check Check the array\n");
|
||||
printf(" fix Fix the array\n");
|
||||
|
|
@ -384,6 +385,7 @@ void signal_handler(int signal)
|
|||
#define OPERATION_SPINUP 13
|
||||
#define OPERATION_SPINDOWN 14
|
||||
#define OPERATION_DEVICES 15
|
||||
#define OPERATION_SMART 16
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
|
@ -743,6 +745,8 @@ int main(int argc, char* argv[])
|
|||
operation = OPERATION_SPINDOWN;
|
||||
} else if (strcmp(argv[optind], "test-devices") == 0) {
|
||||
operation = OPERATION_DEVICES;
|
||||
} else if (strcmp(argv[optind], "smart") == 0) {
|
||||
operation = OPERATION_SMART;
|
||||
} else {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Unknown command '%s'\n", argv[optind]);
|
||||
|
|
@ -861,7 +865,7 @@ int main(int argc, char* argv[])
|
|||
case OPERATION_STATUS :
|
||||
case OPERATION_REWRITE :
|
||||
case OPERATION_REHASH :
|
||||
case OPERATION_SPINUP : /* skip because we want to do it in parallel */
|
||||
case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
|
||||
/* avoid to check and access data disks if not needed */
|
||||
opt.skip_disk_access = 1;
|
||||
break;
|
||||
|
|
@ -876,7 +880,7 @@ int main(int argc, char* argv[])
|
|||
case OPERATION_REWRITE :
|
||||
case OPERATION_REHASH :
|
||||
case OPERATION_NANO :
|
||||
case OPERATION_SPINUP : /* skip because we want to do it in parallel */
|
||||
case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
|
||||
/* avoid to check and access parity disks if not needed */
|
||||
opt.skip_parity_access = 1;
|
||||
break;
|
||||
|
|
@ -891,6 +895,7 @@ int main(int argc, char* argv[])
|
|||
case OPERATION_SPINUP :
|
||||
case OPERATION_SPINDOWN :
|
||||
case OPERATION_DEVICES :
|
||||
case OPERATION_SMART :
|
||||
opt.skip_self = 1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1083,6 +1088,8 @@ int main(int argc, char* argv[])
|
|||
state_device(&state, DEVICE_DOWN);
|
||||
} else if (operation == OPERATION_DEVICES) {
|
||||
state_device(&state, DEVICE_LIST);
|
||||
} else if (operation == OPERATION_SMART) {
|
||||
state_device(&state, DEVICE_SMART);
|
||||
} else if (operation == OPERATION_STATUS) {
|
||||
state_read(&state);
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ int state_status(struct snapraid_state* state)
|
|||
}
|
||||
fprintf(stdlog, "summary:parity_block_free_min:%u\n", parity_block_free);
|
||||
|
||||
printf("SnapRAID status report:\n");
|
||||
printf("\n");
|
||||
printf(" Files Fragmented Excess Wasted Used Free Use Name\n");
|
||||
printf(" Files Fragments GiB GiB GiB \n");
|
||||
|
|
|
|||
188
cmdline/unix.c
188
cmdline/unix.c
|
|
@ -458,7 +458,7 @@ static int devresolve(dev_t device, char* path, size_t path_size)
|
|||
* Read a device tree filling the specified list of disk_t entries.
|
||||
*/
|
||||
#if HAVE_LINUX_DEVICE
|
||||
static int devtree(const char* name, dev_t device, tommy_list* list)
|
||||
static int devtree(const char* name, dev_t device, devinfo_t* parent, tommy_list* list)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
DIR* d;
|
||||
|
|
@ -483,7 +483,7 @@ static int devtree(const char* name, dev_t device, tommy_list* list)
|
|||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (devtree(name, device, list) != 0) {
|
||||
if (devtree(name, device, parent, list) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
|
@ -516,7 +516,7 @@ static int devtree(const char* name, dev_t device, tommy_list* list)
|
|||
}
|
||||
}
|
||||
|
||||
/* get the real name of the device */
|
||||
/* get the device file */
|
||||
if (devresolve(device, path, sizeof(path)) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
|
|
@ -528,6 +528,7 @@ static int devtree(const char* name, dev_t device, tommy_list* list)
|
|||
devinfo->device = device;
|
||||
pathcpy(devinfo->name, sizeof(devinfo->name), name);
|
||||
pathcpy(devinfo->file, sizeof(devinfo->file), path);
|
||||
devinfo->parent = parent;
|
||||
|
||||
/* insert in the list */
|
||||
tommy_list_insert_tail(list, &devinfo->node, devinfo);
|
||||
|
|
@ -537,6 +538,84 @@ static int devtree(const char* name, dev_t device, tommy_list* list)
|
|||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets SMART attributes.
|
||||
*/
|
||||
static int devsmart(dev_t device, uint64_t* smart, uint64_t* size, char* serial)
|
||||
{
|
||||
char cmd[128];
|
||||
FILE* f;
|
||||
int inside;
|
||||
unsigned i;
|
||||
|
||||
*serial = 0;
|
||||
for (i = 0; i < SMART_COUNT; ++i)
|
||||
smart[i] = SMART_UNASSIGNED;
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "smartctl -a /dev/block/%u:%u", major(device), minor(device));
|
||||
|
||||
f = popen(cmd, "r");
|
||||
if (!f) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to run smartctrl -a on device '%u:%u'.\n", major(device), minor(device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
inside = 0;
|
||||
while (1) {
|
||||
char buf[256];
|
||||
char attr[64];
|
||||
unsigned id;
|
||||
unsigned long long raw;
|
||||
char* s;
|
||||
|
||||
s = fgets(buf, sizeof(buf), f);
|
||||
|
||||
if (s == 0)
|
||||
break;
|
||||
|
||||
/* skip initial spaces */
|
||||
while (isspace(*s))
|
||||
++s;
|
||||
|
||||
if (*s == 0) {
|
||||
inside = 0;
|
||||
} else if (strncmp(s, "ID# ", 4) == 0) {
|
||||
inside = 1;
|
||||
} else if (sscanf(s, "Serial Number: %63s", serial) == 1) {
|
||||
} else if (sscanf(s, "User Capacity: %63s", attr) == 1) {
|
||||
*size = 0;
|
||||
for (i = 0; attr[i]; ++i) {
|
||||
if (isdigit(attr[i])) {
|
||||
*size *= 10;
|
||||
*size += attr[i] - '0';
|
||||
}
|
||||
}
|
||||
} else if (inside) {
|
||||
if (sscanf(s, "%u %*s %*s %*u %*u %*u %*s %*s %*s %llu", &id, &raw) != 2) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Invalid smartctrl line '%s'.\n", buf);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
if (id >= SMART_COUNT) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Invalid SMART id '%u'.\n", id);
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
smart[id] = raw;
|
||||
}
|
||||
}
|
||||
|
||||
pclose(f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spin down a specific device.
|
||||
*/
|
||||
|
|
@ -645,7 +724,7 @@ static void* thread_spinup(void* arg)
|
|||
/* set the device number for printing */
|
||||
devinfo->device = st.st_dev;
|
||||
|
||||
/* get the block device */
|
||||
/* get the device file */
|
||||
if (devresolve(devinfo->device, devinfo->file, sizeof(devinfo->file)) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return (void*)-1;
|
||||
|
|
@ -670,12 +749,11 @@ static void* thread_spindown(void* arg)
|
|||
{
|
||||
#if HAVE_LINUX_DEVICE
|
||||
devinfo_t* devinfo = arg;
|
||||
dev_t device = devinfo->device;
|
||||
uint64_t start;
|
||||
|
||||
start = tick_ms();
|
||||
|
||||
if (devdown(device) != 0) {
|
||||
if (devdown(devinfo->device) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return (void*)-1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
|
@ -691,48 +769,20 @@ static void* thread_spindown(void* arg)
|
|||
}
|
||||
|
||||
/**
|
||||
* Lists all the devices.
|
||||
* Thread for spinning down.
|
||||
*/
|
||||
#if HAVE_LINUX_DEVICE
|
||||
static int device_list(tommy_list* list)
|
||||
static void* thread_smart(void* arg)
|
||||
{
|
||||
tommy_node* i;
|
||||
devinfo_t* devinfo = arg;
|
||||
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
tommy_list tree;
|
||||
tommy_node* j;
|
||||
devinfo_t* devinfo = i->data;
|
||||
|
||||
tommy_list_init(&tree);
|
||||
|
||||
/* get the block device */
|
||||
if (devresolve(devinfo->device, devinfo->file, sizeof(devinfo->file)) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* expand the tree of devices */
|
||||
if (devtree(devinfo->name, devinfo->device, &tree) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
tommy_list_foreach(&tree, free);
|
||||
fprintf(stderr, "Failed to expand device '%u:%u'.\n", major(devinfo->device), minor(devinfo->device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
for (j = tommy_list_head(&tree); j != 0; j = j->next) {
|
||||
devinfo_t* entry = j->data;
|
||||
|
||||
printf("%u:%u\t%s\t%u:%u\t%s\t%s\n", major(entry->device), minor(entry->device), entry->file, major(devinfo->device), minor(devinfo->device), devinfo->file, devinfo->name);
|
||||
}
|
||||
|
||||
tommy_list_foreach(&tree, free);
|
||||
if (devsmart(devinfo->device, devinfo->smart, &devinfo->smart_size, devinfo->smart_serial) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
return (void*)-1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int device_thread(tommy_list* list, void* (*func)(void* arg))
|
||||
{
|
||||
|
|
@ -784,16 +834,13 @@ static int device_thread(tommy_list* list, void* (*func)(void* arg))
|
|||
return 0;
|
||||
}
|
||||
|
||||
int devquery(tommy_list* list, int operation)
|
||||
int devquery(tommy_list* high, tommy_list* low, int operation)
|
||||
{
|
||||
tommy_node* i;
|
||||
tommy_list tree;
|
||||
int ret;
|
||||
|
||||
tommy_list_init(&tree);
|
||||
void* (*func)(void* arg) = 0;
|
||||
|
||||
#if HAVE_LINUX_DEVICE
|
||||
if (operation == DEVICE_DOWN || operation == DEVICE_LIST) {
|
||||
if (operation != DEVICE_UP) {
|
||||
struct stat st;
|
||||
/* sysfs and devfs interfaces are required */
|
||||
if (stat("/sys/dev/block", &st) != 0) {
|
||||
|
|
@ -808,20 +855,22 @@ int devquery(tommy_list* list, int operation)
|
|||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
}
|
||||
|
||||
if (operation == DEVICE_LIST)
|
||||
return device_list(list);
|
||||
|
||||
if (operation == DEVICE_DOWN) {
|
||||
/* for each device */
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
for (i = tommy_list_head(high); i != 0; i = i->next) {
|
||||
devinfo_t* devinfo = i->data;
|
||||
|
||||
/* expand the tree of devices */
|
||||
if (devtree(devinfo->name, devinfo->device, &tree) != 0) {
|
||||
/* get the device file */
|
||||
if (devresolve(devinfo->device, devinfo->file, sizeof(devinfo->file)) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
fprintf(stderr, "Failed to resolve device '%u:%u'.\n", major(devinfo->device), minor(devinfo->device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
}
|
||||
|
||||
/* expand the tree of devices */
|
||||
if (devtree(devinfo->name, devinfo->device, devinfo, low) != 0) {
|
||||
/* LCOV_EXCL_START */
|
||||
tommy_list_foreach(&tree, free);
|
||||
fprintf(stderr, "Failed to expand device '%u:%u'.\n", major(devinfo->device), minor(devinfo->device));
|
||||
return -1;
|
||||
/* LCOV_EXCL_STOP */
|
||||
|
|
@ -831,8 +880,8 @@ int devquery(tommy_list* list, int operation)
|
|||
#endif
|
||||
|
||||
if (operation == DEVICE_UP) {
|
||||
/* duplicate the list */
|
||||
for (i = tommy_list_head(list); i != 0; i = i->next) {
|
||||
/* duplicate the high */
|
||||
for (i = tommy_list_head(high); i != 0; i = i->next) {
|
||||
devinfo_t* devinfo = i->data;
|
||||
devinfo_t* entry;
|
||||
|
||||
|
|
@ -842,25 +891,30 @@ int devquery(tommy_list* list, int operation)
|
|||
pathcpy(entry->name, sizeof(entry->name), devinfo->name);
|
||||
pathcpy(entry->mount, sizeof(entry->mount), devinfo->mount);
|
||||
|
||||
/* insert in the list */
|
||||
tommy_list_insert_tail(&tree, &entry->node, entry);
|
||||
/* insert in the high */
|
||||
tommy_list_insert_tail(low, &entry->node, entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* sort the list */
|
||||
tommy_list_sort(&tree, devcompare);
|
||||
/* sort the high */
|
||||
tommy_list_sort(low, devcompare);
|
||||
|
||||
/* removes duplicates */
|
||||
for (i = tommy_list_head(&tree); i != 0; i = i->next) {
|
||||
for (i = tommy_list_head(low); i != 0; i = i->next) {
|
||||
while (i->next != 0 && devcompare(i->data, i->next->data) == 0)
|
||||
free(tommy_list_remove_existing(&tree, i->next));
|
||||
free(tommy_list_remove_existing(low, i->next));
|
||||
}
|
||||
|
||||
ret = device_thread(&tree, operation == DEVICE_UP ? thread_spinup : thread_spindown);
|
||||
switch (operation) {
|
||||
case DEVICE_UP : func = thread_spinup; break;
|
||||
case DEVICE_DOWN : func = thread_spindown; break;
|
||||
case DEVICE_SMART : func = thread_smart; break;
|
||||
}
|
||||
|
||||
tommy_list_foreach(&tree, free);
|
||||
if (!func)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
return device_thread(low, func);
|
||||
}
|
||||
|
||||
void os_init(int opt)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue