Skip spinup and spindown for non-rotational devices

This commit is contained in:
Andrea Mazzoleni 2026-06-15 00:45:45 +02:00
parent d7c6a27c6d
commit d6d1d6d3cd
4 changed files with 112 additions and 8 deletions

View file

@ -32,6 +32,8 @@ SnapRAID HISTORY
will be slower, this change preserves hash data in the content file
to ensure a better fix during disk failures and allows the --import
option to remain functional even after an incomplete sync.
* Skip spin-up and spin-down operations for non-rotational devices
(like SSDs and NVMe drives) on Linux and Windows.
* Added --with-smartctl, --with-zfs, --with-zpool, --with-bcachefs
configure options to allow pinning the absolute path of each tool at
build time. When not specified, the existing runtime discovery

View file

@ -410,7 +410,6 @@ static void devattr_property(HANDLE h, char* serial, char* model, char* inter)
#define BusTypeUfs 0x13
#define BusTypeNvmeof 0x14
/* always override interface as more detailed than smartctl */
const char* type = 0;
switch ((int)desc->BusType) { /* cast to int to avoid warnings about enum unlisted */
case BusTypeScsi : type = "SCSI"; break;
@ -435,7 +434,7 @@ static void devattr_property(HANDLE h, char* serial, char* model, char* inter)
case BusTypeNvmeof : type = "NVMe"; break;
default :
}
if (inter && type)
if (inter && type) /* always override interface as more detailed than smartctl */
snprintf(inter, SMART_MAX, "%s", type);
free(buffer);
@ -483,7 +482,7 @@ static void devattr(uint64_t device, const char* name, const char* wfile, uint64
(void)family; /* not available, smartctl uses an internal database to get it */
/* open the volume */
h = CreateFileW(convert(conv_buf, wfile), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, 0);
h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (h == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
windows_errno(error);
@ -494,12 +493,17 @@ static void devattr(uint64_t device, const char* name, const char* wfile, uint64
if (info[INFO_SIZE] == SMART_UNASSIGNED)
devattr_size(h, &info[INFO_SIZE]);
if (*model == 0 || *serial == 0 || *interf == 0)
devattr_property(h, serial, model, interf);
/* set NVMe as not rotational */
if (info[INFO_ROTATION_RATE] == SMART_UNASSIGNED && strcasecmp(interf, "nvme") == 0)
info[INFO_ROTATION_RATE] = 0;
/* set SSD as not rotational */
if (info[INFO_ROTATION_RATE] == SMART_UNASSIGNED)
devattr_rotational(h, &info[INFO_ROTATION_RATE]);
if (*model == 0 || *serial == 0)
devattr_property(h, serial, model, interf);
if (!CloseHandle(h)) {
DWORD error = GetLastError();
windows_errno(error);
@ -508,6 +512,54 @@ static void devattr(uint64_t device, const char* name, const char* wfile, uint64
}
}
/**
* Check if the device needs power management (it's a rotational one)
*/
static int devpower(uint64_t device, const char* name, const char* wfile)
{
HANDLE h;
wchar_t conv_buf[CONV_MAX];
char file[128];
uint64_t rotational;
char interf[SMART_MAX];
rotational = SMART_UNASSIGNED;
interf[0] = 0;
snprintf(file, sizeof(file), "/dev/pd%" PRIu64, device);
/* open the volume */
h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (h == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
windows_errno(error);
log_tag("device:%s:%s:error:%lu\n", file, name, error);
return 0; /* assume not rotational */
}
/* get the interface */
devattr_property(h, 0, 0, interf);
/* set NVMe as not rotational */
if (strcasecmp(interf, "nvme") == 0)
rotational = 0;
/* set SSD as not rotational */
if (rotational == SMART_UNASSIGNED)
devattr_rotational(h, &rotational);
if (!CloseHandle(h)) {
DWORD error = GetLastError();
windows_errno(error);
log_tag("device:%s:%s:error:%lu\n", file, name, error);
return 0; /* assume not rotational */
}
if (rotational == SMART_UNASSIGNED)
return 0; /* assume not rotational */
return rotational != 0;
}
/**
* Get POWER state
*/
@ -780,6 +832,10 @@ static void* thread_spinup(void* arg)
devinfo_t* devinfo = arg;
uint64_t start;
/* skip not rotational devices */
if (devpower(devinfo->device, devinfo->name, devinfo->wfile) == 0)
return (void*)-1;
start = os_tick_ms();
if (devup(devinfo->device, devinfo->name, devinfo->wfile) != 0) {
@ -816,6 +872,10 @@ 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 (void*)-1;
start = os_tick_ms();
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
@ -990,9 +1050,14 @@ int devmap(void)
HANDLE h;
/* open the volume */
h = CreateFileW(convert(conv_buf, wfile), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH, 0);
if (h == INVALID_HANDLE_VALUE)
h = CreateFileW(convert(conv_buf, wfile), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
if (h == INVALID_HANDLE_VALUE) {
DWORD error = GetLastError();
if (error != ERROR_FILE_NOT_FOUND)
log_tag("enumeration:/dev/pd%d::error:%lu\n", i, error);
continue;
}
char serial[SMART_MAX];
serial[0] = 0;
devattr_property(h, serial, 0, 0);

View file

@ -2514,6 +2514,7 @@ int smartctl_attribute(OS_FILE* f, const char* file, const char* name, struct sm
*/
} else if (smatch(s, "NVMe Version:") == 0) {
strcpy(inter, "NVMe");
info[INFO_ROTATION_RATE] = 0;
/* special entries */
} else if (snumber(s, "Media and Data Integrity Errors:", &raw) == 1) {
smart[SMART_ERROR_MEDIUM].raw = raw;

View file

@ -3218,6 +3218,34 @@ static int devdownifup(dev_t device, const char* name, const char* smartctl, int
}
#endif
/**
* Check if the device needs power management (it's a rotational one)
*/
#if HAVE_LINUX_DEVICE
static int devpower(dev_t device)
{
char path[PATH_MAX];
char buf[512];
uint64_t rotational;
rotational = SMART_UNASSIGNED;
pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/queue/rotational", major(device), minor(device));
if (sysattr(path, buf, sizeof(buf)) == 0) {
char* e;
uint64_t v;
v = strtoul(buf, &e, 10);
if (*e == 0)
rotational = v;
}
if (rotational == SMART_UNASSIGNED)
return 0; /* assume not rotational */
return rotational != 0;
}
#endif
/**
* Spin up a specific device.
*/
@ -3329,6 +3357,10 @@ static void* thread_spinup(void* arg)
devinfo_t* devinfo = arg;
uint64_t start;
/* skip not rotational devices */
if (devpower(devinfo->device) == 0)
return (void*)-1;
start = os_tick_ms();
if (devup(devinfo->device, devinfo->name) != 0) {
@ -3370,6 +3402,10 @@ static void* thread_spindown(void* arg)
devinfo_t* devinfo = arg;
uint64_t start;
/* skip not rotational devices */
if (devpower(devinfo->device) == 0)
return (void*)-1;
start = os_tick_ms();
if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {