Safe to manually spin down hard drive with hdparm?

Posted on

Problem :

I have a laptop with a ssd for primary OS drive, and a large Spinning disk for storage. I currently have an alias I use for manually spinning down the drive when I’m not using it (after umount of course):

sudo hdparm -y /dev/disk/by-id/ata-WDC_WD7500BPKX-80HPJT0_WD-WX31AB3N6985

Is this safe to use? Will hdparm refuse to do this if the drive has yet to sync or if I forget to umount it?
If not, is there a way to use hdparm (or another util) to check if it is safe to do this?


UPDATE —

As much as I appreciate both of your answers, they don’t help me at all, because I’ve tried countless different value combinations for the APM and Spin-down time settings, with no results aside from a) less than 10 seconds before spin-down b) no spin-down but random parking, or c) no spin-down or parking. I’ve also tried to search the web for my specific drive model and its respective APM values, to no avail.

So, the direction I’d like to go in is this:
I need to figure out a way to see if the disk is in use so I can write a systemd service. The closest I can come to so far is lsof | grep /mnt/data, or some such nonesense. But, as you can see, that is far less than ideal. I’d like a method of attaining this that doesn’t depend on a predetermined mount point, or the cpu greedy multiple invocations of lsof +D mntpoint in the answer I provided myself.

Check out my answer below for an idea of what I’m trying to do

Solution :

Instead of manually trying to figure this out, rely on existing stuff. I use hd-idle on my file server, it works great. I also don’t unmount my drives because there’s really no reason to do so. If I want to access a file, the disks will spin up. Then, after the timeout passes, they’ll spin down again.

If you’re super paranoid about data integrity, you can use autofs, which can automatically mount and umount your filesystems on an as-needed basis. I haven’t used it myself, but the ArchWiki page offers some info that looks quite usable.

You have an alias! Maybe it’s better to have a script that does sync / unmount and then calls hdparm. Add the script to the sudoers file.

cat >/usr/local/sbin/spindown
disk=/dev/disk/by-uuid/c542a956-fbfe-42f7-ac8f-44c029a35a69
sync && unmount /bigdisk/volume &&
hdparam -y $disk

The && makes sure the previous command exits successfully before trying the next.

Then add to sudoers. This might work:

cat >/etc/sudoers.d/spindown <<EOF
ALL ALL=(ALL) NOPASSWD:/usr/local/sbin/spindown
EOF

DanielB made a brilliant suggestion: Use autofs to automatically mount/unmount a local disk (I’ve never thought of using it that way since I usually work on servers and since autofs was traditionally for NFS). Then once that’s set up (don’t ask ME how;) ), add something like this to a cronjob:

cat >/etc/cron.d/spindown << EOF
* * * * *  hdparm -C | grep -q -E "idle|active" DISK-DEV && grep -q MOUNTPOINT /etc/mtab && hdparm -y DISK-DEV
EOF

Be sure to replace DISK-DEV and MOUNTPOINT (if Disk-dev has 0 or 1 partition, you can use it for MOUNTPOINT). Please note: I cannot actually test this since the one physical system I have access to ATM is a RAID device and the controller doesn’t support those commands.

Note

You don’t need to umount the partitions in order to spin down the hard drive. It will be easier if you just set a standby timeout (that will trigger after some time not being used). This way, when needed, the disk will awake (spin-up) and you’ll be able to use it straight away without the need to remount the partitions.

Command-line solution

As @Nattgew has suggested on a comment, use hdparm (if that’s the tool you like to use) with the -S option to set a standby timeout:

-S   Set standby (spindown) timeout

This way the disk will automatically spin down when there’s no activity.

In order to make this change permanent (i.e. after reboot), you may need to make changes to /etc/hdparm.conf. Or maybe, as your distribution is likely nowadays using systemd, you may need to create a unit:

https://unix.stackexchange.com/a/89013/66295

Probably the easiest way would be to use a graphical tool… (yeah, sometimes, when you already have a graphical interface enabled in your system, it is the fastest way)

GUI solution (GNOME)

As you are saying you are using your laptop, I assume you actually have a graphical interface. With GNOME distributions, there’s a program named “Disks”:

enter image description here

Once you select the hard drive you want to put in standby mode, you can click on “Drive settings” from the menu (or CTRL + E may be a shortcut as well):

enter image description here

Then change the standby timeout settings:

enter image description here

Here is my hack of a script so far. I will keep this open until somebody finds a better solution…

#!/bin/bash

# for each disk specified as disk
#   if disk is active/idle
#     disk in use as inuse = false
#     for each mount point as mntpoint accociated with it
#       if (lsof | grep mntpoint)
#         inuse = true
#         break
#       endif
#     endfor
#     if inuse
#       spin down disk
#     endif
#   endif
# endfor

DISKS_LEN=1
DISKS[1]=ata-WDC_WD7500BPKX-80HPJT0_WD-WX31AB3N6985

spindown_disk() {
    (sync $1 && sudo hdparm -y /dev/$1 >/dev/null) &
}

diskArr_len=0
for i in `seq $DISKS_LEN`; do
    linkpath=$(readlink /dev/disk/by-id/${DISKS[$i]})
    ((diskArr_len+=1))
    diskArr[$i]=${linkpath##*/}
done

for i in `seq $diskArr_len`; do
    disk=${diskArr[$i]}
    if (sudo hdparm -C /dev/$disk | grep -B1 'active/idle'); then
        inuse=false
        mntpoints="$(mktemp)"
        mount 
            | grep "^/dev/$disk[0-9]" 
            | sed -r "s|^/dev/$disk[0-9] on (.*) type.*$|1|" 
            >"$mntpoints"
        while read mntpoint; do
            while read openfile; do
                [ $inuse = false ] && echo open files:
                echo $'t'/${openfile#*/}
                inuse=true
            done < <(lsof | grep "$mntpoint")
        done <"$mntpoints"
        rm -f "$mntpoints"
        if [ "$inuse" = true ]; then
            echo $disk is in use
        else
            echo spinning down $disk (after sync)
            spindown_disk $disk
        fi
    else
        echo $disk is already inactive
    fi
done

exit 0

I will still have to figure out how to wrap this up in a systemd service that runs every X minutes. Obviously it would be better if there was an alternative to using lsof and grep. Also, I’m not sure if this would work with funny mount point characters because of the use of grep. Also, it would need to be revised if you had more than 9 partitions.

Please, if you have ANY suggestions don’t hesitate.

Leave a Reply

Your email address will not be published. Required fields are marked *