From d6d1d6d3cd70903cc175a3b2e33b249cd0888f28 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Mon, 15 Jun 2026 00:45:45 +0200 Subject: [PATCH] Skip spinup and spindown for non-rotational devices --- HISTORY | 2 ++ cmdline/mingwapp.c | 81 +++++++++++++++++++++++++++++++++++++++++----- cmdline/support.c | 1 + cmdline/unixapp.c | 36 +++++++++++++++++++++ 4 files changed, 112 insertions(+), 8 deletions(-) diff --git a/HISTORY b/HISTORY index eca904db..bf5054b7 100644 --- a/HISTORY +++ b/HISTORY @@ -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 diff --git a/cmdline/mingwapp.c b/cmdline/mingwapp.c index 8bd0136d..320fd4e0 100644 --- a/cmdline/mingwapp.c +++ b/cmdline/mingwapp.c @@ -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); diff --git a/cmdline/support.c b/cmdline/support.c index f3f82a10..df83bbb1 100644 --- a/cmdline/support.c +++ b/cmdline/support.c @@ -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; diff --git a/cmdline/unixapp.c b/cmdline/unixapp.c index 3e3337a1..a6375845 100644 --- a/cmdline/unixapp.c +++ b/cmdline/unixapp.c @@ -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) {