Script to spindown unassigned USB HDDs


Recommended Posts

I have a USB HDD in unassigned devices which I use for backups. The drive won't spin down for whatever reason, mounted or not.

 

My solution was a modified version of this script that I have User Scripts run every hour.

It takes a list of drives, checks for activity and spins them down if they're inactive and spun up.

 

I thought I'd share it.

 

NOTE: The green "led" in Unassigned Devices stays green even when the drive's spun down, I don't know how or if it detects power status.

#!/bin/bash
# This script looks for recent disk access, and if nothing has changed, puts /dev/disk/by-id/${drive} into spindown mode.
#
# Set your drive identifiers (listed in /dev/disk/by-id/) ignoring characters after the last "-"
# e.g. listing: usb-WD_My_Passport_25E2_75831314363630505A37-0:0
#   becomes: usb-WD_My_Passport_25E2_75831314363630505A37
drives=(
  "<DRIVE_IDENTIFIER_1>"
  "<DRIVE_IDENTIFIER_2>"
)

# spindown delay in minutes
SPINDOWN_DELAY=30

# Uncomment to enable debug mode
#DEBUG=true

# Set the directory where the status files will be stored,
# /tmp/ is a fine default
STATUS_DIR="/tmp"

current=`date`

# create status_dir if it doesn't exist
mkdir -p ${STATUS_DIR}

do_device() {
    local device_id=$1
    device=`ls -l /dev/disk/by-id/ | grep ${device_id} | head -1 | tail -c4`
    filename="${STATUS_DIR}/diskaccess-${device_id}.status"

    is_awake=`smartctl --nocheck standby -i /dev/${device} | grep 'Power mode is' | egrep -c 'ACTIVE|IDLE'`
    if [ "${is_awake}" == "1" ]; then
        if [ "$DEBUG" = true ]; then
            echo "${device} is awake"
        fi

        stat_new=$(grep "${device}1 " /proc/diskstats | tr -dc "[:digit:]")

        if [ ! -f "${filename}" ]; then
            echo ${current} "- ${filename} file does not exist; creating it now."
            echo ${stat_new} > ${filename}
        else
            stat_old=`cat ${filename} | tr -dc "[:digit:]"`

            # calculate minutes since last update to see if we should sleep
            current_time=$(date +%s)
            last_mod=$(stat ${filename} -c %Y)
            seconds_ago=$(expr $current_time - $last_mod)
            minutes_ago=$(expr $seconds_ago / 60)

            if [ "$DEBUG" = true ]; then
                echo "${device} old stat: ${stat_old}"
                echo "${device} new stat: ${stat_new}"
                echo "${device} new stat modified ${minutes_ago} minutes ago"
            fi

            if [ "${stat_old}" == "${stat_new}" ]; then
                if [ $minutes_ago -ge $SPINDOWN_DELAY ]; then
                    echo ${current} "- Drive /dev/${device} is awake and hasn't been used in ${minutes_ago} minutes; spinning down"
                    hdparm -y /dev/${device} > /dev/null 2>&1
                else
                    echo ${current} "- Drive /dev/${device} was last used ${minutes_ago} minutes ago, less than spindown setting ($SPINDOWN_DELAY)"
                fi
            else
                echo ${current} "- Drive /dev/${device} has been used..."
                echo ${stat_new} > ${filename}
            fi
        fi
    else
        if [ "$DEBUG" = true ]; then
            echo "${device} is asleep"
        fi
    fi
}

for device_id in ${drives[*]}
do
    do_device ${device_id}
done

 

 

Edited by CS01-HS
updated to work with 6.9.0-beta30
  • Like 1
Link to comment

Updated the script to work with with 6.9.0-beta30

 

I don't know if it's the new kernel or unraid itself but now the drive's diskstats seem to increment even without access (maybe SMART polling?) Keying off the partition's diskstats seems to solve it. Note, this looks for reads/writes on the first partition - if you have a multi-partition drive it won't work properly (and may sleep you drive while you're accessing it,)

Link to comment
  • 9 months later...

Thanks for the great script.

But my unraid has problem that the USB HDD spin up unexpected every hours, so the script runs every hours and I can see the HDD is always awake in the script log.  I have already unmounted the USB HDD but don't know why it is still awaken frequently. Any suggestions?

Link to comment
On 8/11/2021 at 9:18 PM, jinlife said:

Thanks for the great script.

But my unraid has problem that the USB HDD spin up unexpected every hours, so the script runs every hours and I can see the HDD is always awake in the script log.  I have already unmounted the USB HDD but don't know why it is still awaken frequently. Any suggestions?

 

You say the USB HDD spins up every hour, so it must be spinning down.

Is it spinning down on its own or is my script spinning it down?

 

You could try disabling the USB HDD's internal power management:

Find the disk's ID with:

find /dev/disk/by-id/ ! -name '*-part1'

 

The line for your USB disk will look like:

/dev/disk/by-id/usb-XXXXXX

 

Take that and disable power management with the command:

/usr/sbin/smartctl -s apm,off /dev/disk/by-id/usb-XXXXXX

 

If that fixes it add the command exactly the way you ran it to your /boot/config/go because reboot resets it.

 

 

Edited by CS01-HS
Link to comment

I use the script to spin down the disk and the disk was also unmounted before, so there should be no activity on the disk. But it always spin up unexpectedly, so even I scheduled to run the script every hour, the disk is always awaken.  It is endless loop: up, down, up, down ...

I will try to disable power management and get back to you about the result. Really appreciate for the help.

Link to comment

Bad news, it still work like before even disabled the power management.

Run this script and disk will spin down, but the standy mode will only last for 20~30 minutes, then it will spin up unexpectly, then the script will spin down it again... There is no log in syslog. 

Edited by jinlife
Link to comment

You can't spin the disk down that way.  Unraid controls the spin up/down of the disk in later versions of 6.9 and 6.10.  You are confusing Unraid.  Go to the UD web page and look at the help you'll see a command to spin down the disk using the UD script that uses the Unraid api.  Unraid then knows the proper spin status.

Link to comment
3 hours ago, dlandon said:

You can't spin the disk down that way.  Unraid controls the spin up/down of the disk in later versions of 6.9 and 6.10.  You are confusing Unraid.  Go to the UD web page and look at the help you'll see a command to spin down the disk using the UD script that uses the Unraid api.  Unraid then knows the proper spin status.

 

Thanks, I see this command in help:

/usr/local/sbin/rc.unassigned spindown devX

 

I wish I'd known about it earlier, still the script worked well for me in 6.8 and 6.9.

 

4 hours ago, jinlife said:

Bad news, it still work like before even disabled the power management.

Run this script and disk will spin down, but the standy mode will only last for 20~30 minutes, then it will spin up unexpectly, then the script will spin down it again... There is no log in syslog. 

 

jinlife:

I'd say disable the script for now, unmount the drive, run the command above where "devX is the device name in the UD page. If the device name is 'Dev 1', then use dev1 as the device to spin down."

 

 

Edited by CS01-HS
corrected per post below
Link to comment

You didn't do the command correctly:

/usr/local/sbin/rc.unassigned spindown devX' - spin down a disk. The devX is the device name in the UD page. If the device name is 'Dev 1', then use dev1 as the device to spin down. SSDs will not spin down.

Use devX, not sdX.  Besides being incorrect for rc.unassigned, the sdX can change after a reboot and you'd reference the wrong disk.

 

The spin down of UD disks was changed in a later version of 6.9rc.

  • Thanks 1
Link to comment
On 8/13/2021 at 8:08 AM, jinlife said:

Good to know that. Thanks guys. 

The script is good for old Unraid versions. I will try the new command. Thanks again.

 

Just add some comments here, in case someone has same requirement, I want to spindown my NTFS formated USB HDD mounted by UHD plugin.

1.  Unraid 6.8.x with this script, it works, but it will spinup by Unraid after 20~30 minutes later. No log at all.

2. Unraid 6.9.2, no need to use this script. Unraid will manage USB HDD automatically, including Array and Unassigned disk. But it will spinup USB HDD after 20~30 minutes with log "emhttpd: read SMART". Looks like tons of complaints in the forum with no solution but revert back to 6.9.1

3. Unraid 6.9.1, just works like 6.9.2 but it won't spinup USB HDD, it is the perfect solution to me now.

 

Edited by jinlife
  • Like 1
Link to comment

To add further complication behavior seems to depend on the particular USB drive/controller. This script worked (and was necessary) for me for all 6.8.X and 6.9.X versions. I don't know about 6.10 because I've switched from USB to an odroid HC2 (nice little machine.)

 

If I remember right unraid had issues tracking spindown state because my drive (WD Passport) would exit with an error code from hdparm -C /dev/sdX - something about "bad/missing sense" but that indicated it was actually spun down. smartctl worked properly however. To work around it I modified unRAID's sdspin to ignore that particular error and prefer smartctl to hdparm with USB drives. In /boot/config/go I put:

# Custom sdspin (to handle substandard USB drive controller)
cp /usr/local/sbin/sdspin /usr/local/sbin/sdspin.unraid
cp /boot/extras/utilities/sdspin /usr/local/sbin/sdspin
chmod 755 /usr/local/sbin/sdspin

 

where /boot/extras/utilities/sdspin was my customized version:

#!/bin/bash

# spin device up or down or get spinning status
# $1 device name
# $2 up or down or status
# ATA only

# hattip to Community Dev @doron

RDEVNAME=/dev/${1#'/dev/'}      # So that we can be called with either "sdX" or "/dev/sdX"

get_device_id () {
  LABEL="${RDEVNAME:5}"
  DEVICE_ID=`ls -l /dev/disk/by-id/ | grep -v " wwn-" | grep "${LABEL}$" | rev | cut -d ' ' -f3 | rev`
  echo "$DEVICE_ID"
}

smartctl_status () {
  OUTPUT=$(/usr/sbin/smartctl --nocheck standby -i $RDEVNAME 2>&1)
  RET=$?
  # Ignore Bit 1 error (Device open failed) which usually indicates standby
  [[ $RET == 2 && $(($RET & 2)) == 2 ]] && RET=0
}

hdparm () {
  OUTPUT=$(/usr/sbin/hdparm $1 $RDEVNAME 2>&1)
  RET=$?
  # ignore missing sense warning which might be caused by a substandard USB interface
  if [[ ! "$(get_device_id)" =~ ^usb-.* ]]; then
    [[ $RET == 0 && ${OUTPUT,,} =~ "bad/missing sense" ]] && RET=1
  fi
}    

if [[ "$2" == "up" ]]; then
  hdparm "-S0"
elif [[ "$2" == "down" ]]; then
  hdparm "-y"
else
  # use smartctl (instead of hdparm) for USB drives
  if [[ "$(get_device_id)" =~ ^usb-.* ]]; then
    smartctl_status
  else
    hdparm "-C"
  fi
  [[ $RET == 0 && ${OUTPUT,,} =~ "standby" ]] && RET=2
fi

 

Very hacky.

Link to comment
  • 2 years later...
On 5/22/2020 at 4:43 PM, CS01-HS said:

I have a USB HDD in unassigned devices which I use for backups. The drive won't spin down for whatever reason, mounted or not.

 

My solution was a modified version of this script that I have User Scripts run every hour.

It takes a list of drives, checks for activity and spins them down if they're inactive and spun up.

 

I thought I'd share it.

 

NOTE: The green "led" in Unassigned Devices stays green even when the drive's spun down, I don't know how or if it detects power status.

#!/bin/bash
# This script looks for recent disk access, and if nothing has changed, puts /dev/disk/by-id/${drive} into spindown mode.
#
# Set your drive identifiers (listed in /dev/disk/by-id/) ignoring characters after the last "-"
# e.g. listing: usb-WD_My_Passport_25E2_75831314363630505A37-0:0
#   becomes: usb-WD_My_Passport_25E2_75831314363630505A37
drives=(
  "<DRIVE_IDENTIFIER_1>"
  "<DRIVE_IDENTIFIER_2>"
)

# spindown delay in minutes
SPINDOWN_DELAY=30

# Uncomment to enable debug mode
#DEBUG=true

# Set the directory where the status files will be stored,
# /tmp/ is a fine default
STATUS_DIR="/tmp"

current=`date`

# create status_dir if it doesn't exist
mkdir -p ${STATUS_DIR}

do_device() {
    local device_id=$1
    device=`ls -l /dev/disk/by-id/ | grep ${device_id} | head -1 | tail -c4`
    filename="${STATUS_DIR}/diskaccess-${device_id}.status"

    is_awake=`smartctl --nocheck standby -i /dev/${device} | grep 'Power mode is' | egrep -c 'ACTIVE|IDLE'`
    if [ "${is_awake}" == "1" ]; then
        if [ "$DEBUG" = true ]; then
            echo "${device} is awake"
        fi

        stat_new=$(grep "${device}1 " /proc/diskstats | tr -dc "[:digit:]")

        if [ ! -f "${filename}" ]; then
            echo ${current} "- ${filename} file does not exist; creating it now."
            echo ${stat_new} > ${filename}
        else
            stat_old=`cat ${filename} | tr -dc "[:digit:]"`

            # calculate minutes since last update to see if we should sleep
            current_time=$(date +%s)
            last_mod=$(stat ${filename} -c %Y)
            seconds_ago=$(expr $current_time - $last_mod)
            minutes_ago=$(expr $seconds_ago / 60)

            if [ "$DEBUG" = true ]; then
                echo "${device} old stat: ${stat_old}"
                echo "${device} new stat: ${stat_new}"
                echo "${device} new stat modified ${minutes_ago} minutes ago"
            fi

            if [ "${stat_old}" == "${stat_new}" ]; then
                if [ $minutes_ago -ge $SPINDOWN_DELAY ]; then
                    echo ${current} "- Drive /dev/${device} is awake and hasn't been used in ${minutes_ago} minutes; spinning down"
                    hdparm -y /dev/${device} > /dev/null 2>&1
                else
                    echo ${current} "- Drive /dev/${device} was last used ${minutes_ago} minutes ago, less than spindown setting ($SPINDOWN_DELAY)"
                fi
            else
                echo ${current} "- Drive /dev/${device} has been used..."
                echo ${stat_new} > ${filename}
            fi
        fi
    else
        if [ "$DEBUG" = true ]; then
            echo "${device} is asleep"
        fi
    fi
}

for device_id in ${drives[*]}
do
    do_device ${device_id}
done

 

 

I tried this script and I noticed that doesn't detect properly when the drive is being used and every time I try to run it the drives are spinning down and up.

I use unraid 6.12.6.

After further investigation I changed 

stat_new=$(grep "${device}1 " /proc/diskstats | tr -dc "[:digit:]")

to 

stat_new=$(grep "${device} " /proc/diskstats | tr -dc "[:digit:]")

In this way it search for ativity on the drive not partition and like this is working properly.

  • Thanks 1
Link to comment
  • 3 weeks later...
On 1/25/2024 at 4:22 AM, sergiu.topan said:

In this way it search for ativity on the drive not partition and like this is working properly.

 

That's how I had it initially:

On 10/12/2020 at 6:05 PM, CS01-HS said:

Updated the script to work with with 6.9.0-beta30

 

I don't know if it's the new kernel or unraid itself but now the drive's diskstats seem to increment even without access (maybe SMART polling?) Keying off the partition's diskstats seems to solve it.

 

But I haven't used this script in years (I eliminated the USB drive) so much may have changed.

If it works, it works!

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.