Query SMART attributes for all disks in spinup while restricting power operations to array members
Some checks are pending
Build / build-mac (push) Waiting to run
Build / build-linux (push) Waiting to run
Build / build-arm32 (push) Waiting to run
Build / build-win (push) Waiting to run
Build / build-freebsd (push) Waiting to run
Build / build-musl (push) Waiting to run
CodeQL Advanced / Analyze (c-cpp) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run

This commit is contained in:
Andrea Mazzoleni 2026-09-23 06:56:15 +02:00
parent 5b615a0e02
commit e481b5f207
3 changed files with 141 additions and 126 deletions

View file

@ -662,7 +662,7 @@ static void state_smart(struct snapraid_state* state, size_t n, tommy_list* low)
if (len > serial_pad)
serial_pad = len;
if (devinfo->parent && devinfo->parent->is_array)
if (devinfo->is_array)
have_array = 1;
}
@ -756,10 +756,8 @@ static void state_smart(struct snapraid_state* state, size_t n, tommy_list* low)
/* this happens only if no data */
printf(" -");
} else {
int is_array = devinfo->parent && devinfo->parent->is_array;
/* compute failure rate only for array disks (or fallback to all disks if none are array members) */
if (is_array || !have_array)
if (devinfo->is_array || !have_array)
array_failure_rate += afr;
printf("%4.0f%%", poisson_prob_at_least_one_failure(afr) * 100);
@ -1032,6 +1030,7 @@ int devtest(tommy_list* high, tommy_list* low, int operation)
/* fake device number */
entry->device = tommy_strhash_u32(0, devinfo->name);
entry->is_array = devinfo->is_array;
tommy_list_insert_tail(low, &entry->node, entry);
@ -1162,36 +1161,25 @@ int state_device(struct snapraid_state* state, int operation, tommy_list* filter
}
}
/* extra disks are never spundown or spunup */
int include_extra = 1;
switch (operation) {
case DEVICE_UP :
case DEVICE_DOWN :
case DEVICE_DOWNIFUP :
include_extra = 0;
break;
}
if (include_extra) {
/* for all extra disks */
for (i = state->extralist; i != 0; i = i->next) {
struct snapraid_extra* extra = i->data;
devinfo_t* entry;
/* for all extra disks */
for (i = state->extralist; i != 0; i = i->next) {
struct snapraid_extra* extra = i->data;
devinfo_t* entry;
if (filterlist_disk != 0 && filter_path(filterlist_disk, 0, extra->name, 0) != 0)
continue;
if (filterlist_disk != 0 && filter_path(filterlist_disk, 0, extra->name, 0) != 0)
continue;
entry = calloc_nofail(1, sizeof(devinfo_t));
entry = calloc_nofail(1, sizeof(devinfo_t));
entry->device = extra->device;
device_name_set(entry, extra->name, 0);
entry->is_array = 0;
pathcpy(entry->mount, sizeof(entry->mount), extra->dir);
pathcpy(entry->smartctl, sizeof(entry->smartctl), extra->smartctl);
pathcpy(entry->smartctl_info, sizeof(entry->smartctl_info), extra->smartctl_info);
memcpy(entry->smartignore, extra->smartignore, sizeof(entry->smartignore));
entry->device = extra->device;
device_name_set(entry, extra->name, 0);
entry->is_array = 0;
pathcpy(entry->mount, sizeof(entry->mount), extra->dir);
pathcpy(entry->smartctl, sizeof(entry->smartctl), extra->smartctl);
pathcpy(entry->smartctl_info, sizeof(entry->smartctl_info), extra->smartctl_info);
memcpy(entry->smartignore, extra->smartignore, sizeof(entry->smartignore));
tommy_list_insert_tail(&high, &entry->node, entry);
}
tommy_list_insert_tail(&high, &entry->node, entry);
}
/* with a GUI always gives time reference */
@ -1199,6 +1187,7 @@ int state_device(struct snapraid_state* state, int operation, tommy_list* filter
log_tag("unixtime:%" PRIi64 "\n", (int64_t)now);
int degraded = 0;
int query_failed = 0;
if (state->opt.fake_device) {
ret = devtest(&high, &low, operation);
@ -1211,6 +1200,10 @@ int state_device(struct snapraid_state* state, int operation, tommy_list* filter
if (ret > 0) {
degraded = 1;
ret = 0;
} else if (ret < 0 && !tommy_list_empty(&low)) {
/* keep operating on devices resolved before or after a failed disk */
query_failed = 1;
ret = 0;
}
}
@ -1259,6 +1252,8 @@ bail:
if (ret == 0 && degraded)
ret = 1;
if (ret == 0 && query_failed)
ret = -1;
return ret;
}

View file

@ -1049,6 +1049,7 @@ static int devtree(devinfo_t* parent, tommy_list* list)
pathprint(devinfo->file, sizeof(devinfo->file), "/dev/pd%" PRIu64, devinfo->device);
pathprint(devinfo->wfile, sizeof(devinfo->wfile), "\\\\.\\PhysicalDrive%" PRIu64, devinfo->device);
devinfo->parent = parent;
devinfo->is_array = parent->is_array;
/* insert in the list */
tommy_list_insert_tail(list, &devinfo->node, devinfo);
@ -1959,27 +1960,34 @@ static int devup(uint64_t device, const char* name, const char* wfile)
static void* thread_spinup(void* arg)
{
devinfo_t* devinfo = arg;
uint64_t start;
int ret;
/* skip not rotational devices */
if (devpower(devinfo->device, devinfo->name, devinfo->wfile) == 0)
return 0;
/* only for members of the array and rotational devices */
if (devinfo->is_array && devpower(devinfo->device, devinfo->name, devinfo->wfile) != 0) {
uint64_t start = os_tick_ms();
start = os_tick_ms();
if (devup(devinfo->device, devinfo->name, devinfo->wfile) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
if (devup(devinfo->device, devinfo->name, devinfo->wfile) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
/* after the spin up, get SMART info */
if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
ret = devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
if (ret != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
} else {
/* just probe others */
ret = devprobe(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, &devinfo->power, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
if (ret != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
}
/*
@ -1999,22 +2007,20 @@ static void* thread_spinup(void* arg)
static void* thread_spindown(void* arg)
{
devinfo_t* devinfo = arg;
uint64_t start;
/* skip not rotational devices */
if (devpower(devinfo->device, devinfo->name, devinfo->wfile) == 0)
return 0;
/* only for members of the array and rotational devices */
if (devinfo->is_array && devpower(devinfo->device, devinfo->name, devinfo->wfile) != 0) {
uint64_t start = os_tick_ms();
start = os_tick_ms();
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
}
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
return 0;
}
@ -2024,24 +2030,22 @@ static void* thread_spindown(void* arg)
static void* thread_spindownifup(void* arg)
{
devinfo_t* devinfo = arg;
uint64_t start;
int power;
/* skip not rotational devices */
if (devpower(devinfo->device, devinfo->name, devinfo->wfile) == 0)
return 0;
/* only for members of the array and rotational devices */
if (devinfo->is_array && devpower(devinfo->device, devinfo->name, devinfo->wfile) != 0) {
uint64_t start = os_tick_ms();
int power;
start = os_tick_ms();
if (devdownifup(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, &power) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
if (devdownifup(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, &power) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
if (power == POWER_ACTIVE)
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
}
if (power == POWER_ACTIVE)
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
return 0;
}
@ -2133,6 +2137,7 @@ void devsync(tommy_list* high)
int devquery(tommy_list* high, tommy_list* low)
{
tommy_node* i;
int failed = 0;
/* for each device */
for (i = tommy_list_head(high); i != 0; i = i->next) {
@ -2142,7 +2147,8 @@ int devquery(tommy_list* high, tommy_list* low)
if (devresolve(devinfo->mount, devinfo->file, sizeof(devinfo->file), devinfo->wfile, sizeof(devinfo->wfile)) != 0) {
/* LCOV_EXCL_START */
log_error(EEXTERNAL, "Failed to resolve path '%s'.\n", devinfo->mount);
return -1;
failed = 1;
continue;
/* LCOV_EXCL_STOP */
}
@ -2159,12 +2165,13 @@ int devquery(tommy_list* high, tommy_list* low)
if (devtree(devinfo, low) != 0) {
/* LCOV_EXCL_START */
log_error(EEXTERNAL, "Failed to expand device '%s'.\n", devinfo->file);
return -1;
failed = 1;
continue;
/* LCOV_EXCL_STOP */
}
}
return 0;
return failed ? -1 : 0;
}
int devrun(tommy_list* low, int operation)

View file

@ -3181,6 +3181,7 @@ static int devtree(devinfo_t* parent, dev_t device, tommy_list* list, int* degra
char path[PATH_MAX];
DIR* d;
int slaves = 0;
int failed = 0;
/* check if this device or parent partition is a degraded MD array */
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/md/degraded", major(device), minor(device));
@ -3201,6 +3202,7 @@ static int devtree(devinfo_t* parent, dev_t device, tommy_list* list, int* degra
while ((dd = readdir(d)) != 0) {
if (dd->d_name[0] != '.') {
dev_t subdev;
++slaves;
/* for each slave, expand the full potential tree */
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves/%s/dev", major(device), minor(device), dd->d_name);
@ -3208,19 +3210,17 @@ static int devtree(devinfo_t* parent, dev_t device, tommy_list* list, int* degra
subdev = devread(path);
if (!subdev) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
failed = 1;
continue;
/* LCOV_EXCL_STOP */
}
if (devtree(parent, subdev, list, degraded) != 0) {
/* LCOV_EXCL_START */
closedir(d);
return -1;
failed = 1;
continue;
/* LCOV_EXCL_STOP */
}
++slaves;
}
}
@ -3263,12 +3263,14 @@ static int devtree(devinfo_t* parent, dev_t device, tommy_list* list, int* degra
memcpy(devinfo->smartignore, parent->smartignore, sizeof(devinfo->smartignore));
pathcpy(devinfo->file, sizeof(devinfo->file), path);
devinfo->parent = parent;
devinfo->is_array = parent->is_array;
/* insert in the list */
tommy_list_insert_tail(list, &devinfo->node, devinfo);
}
return 0;
/* retain resolved siblings even when another branch failed */
return failed ? -1 : 0;
}
#endif
@ -3896,27 +3898,34 @@ static void* thread_spinup(void* arg)
{
#if HAVE_LINUX_DEVICE
devinfo_t* devinfo = arg;
uint64_t start;
int ret;
/* skip not rotational devices */
if (devpower(devinfo->device) == 0)
return 0;
/* only for members of the array and rotational devices */
if (devinfo->is_array && devpower(devinfo->device) != 0) {
uint64_t start = os_tick_ms();
start = os_tick_ms();
if (devup(devinfo->device, devinfo->name) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
if (devup(devinfo->device, devinfo->name) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
/* after the spin up, get SMART info */
if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
ret = devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
if (ret != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
} else {
/* just probe others */
ret = devprobe(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, &devinfo->power, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
if (ret != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
}
/*
@ -3941,22 +3950,20 @@ static void* thread_spindown(void* arg)
{
#if HAVE_LINUX_DEVICE
devinfo_t* devinfo = arg;
uint64_t start;
/* skip not rotational devices */
if (devpower(devinfo->device) == 0)
return 0;
/* only for members of the array and rotational devices */
if (devinfo->is_array && devpower(devinfo->device) != 0) {
uint64_t start = os_tick_ms();
start = os_tick_ms();
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
}
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
return 0;
#else
(void)arg;
@ -3971,24 +3978,22 @@ static void* thread_spindownifup(void* arg)
{
#if HAVE_LINUX_DEVICE
devinfo_t* devinfo = arg;
uint64_t start;
int power;
/* skip not rotational devices */
if (devpower(devinfo->device) == 0)
return 0;
/* only for members of the array and rotational devices */
if (devinfo->is_array && devpower(devinfo->device) != 0) {
uint64_t start = os_tick_ms();
int power;
start = os_tick_ms();
if (devdownifup(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, &power) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
}
if (devdownifup(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smartctl_info, &power) != 0) {
/* LCOV_EXCL_START */
return (void*)-1;
/* LCOV_EXCL_STOP */
if (power == POWER_ACTIVE)
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
}
if (power == POWER_ACTIVE)
msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
return 0;
#else
(void)arg;
@ -4103,6 +4108,10 @@ void devsync(tommy_list* high)
devinfo_t* devinfo = i->data;
int f;
/* only for members of the array */
if (!devinfo->is_array)
continue;
f = open(devinfo->mount, O_RDONLY);
if (f >= 0) {
syncfs(f);
@ -4117,6 +4126,7 @@ void devsync(tommy_list* high)
int devquery(tommy_list* high, tommy_list* low)
{
int degraded = 0;
int failed = 0;
#if HAVE_LINUX_DEVICE
tommy_node* i;
struct stat st;
@ -4142,7 +4152,8 @@ int devquery(tommy_list* high, tommy_list* low)
if (ret < 0) {
/* LCOV_EXCL_START */
log_error(EEXTERNAL, "Failed to dereference device '%u:%u' at '%s'.\n", major(device), minor(device), devinfo->mount);
return -1;
failed = 1;
continue;
/* LCOV_EXCL_STOP */
}
@ -4167,7 +4178,8 @@ int devquery(tommy_list* high, tommy_list* low)
if (devresolve(dev->device, file, sizeof(file)) != 0) {
/* LCOV_EXCL_START */
log_error(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(dev->device), minor(dev->device));
return -1;
failed = 1;
continue;
/* LCOV_EXCL_STOP */
}
@ -4180,7 +4192,8 @@ int devquery(tommy_list* high, tommy_list* low)
if (devtree(devinfo, dev->device, low, &disk_degraded) != 0) {
/* LCOV_EXCL_START */
log_error(EEXTERNAL, "Failed to expand device '%u:%u'.\n", major(dev->device), minor(dev->device));
return -1;
failed = 1;
continue;
/* LCOV_EXCL_STOP */
}
}
@ -4198,7 +4211,7 @@ int devquery(tommy_list* high, tommy_list* low)
(void)low;
#endif
return degraded ? 1 : 0;
return failed ? -1 : degraded ? 1 : 0;
}
int devrun(tommy_list* low, int operation)