This method is originally explained in http://gentoo-wiki.com/HOWTO_HDD_spindown_small_server. I have just modified the needed scripts to work on MBWE.
There seems to be quite a few copies of earlier versions of this guide floating around the internets, so make sure you don't use outdated versions of the scripts. This page will always have the most up-to-date versions.
A text editor and the know-how to use it. I recommend installing nano
, but vi
will also do just fine.
Make sure you are in superuser mode.
# su
Update hdparm
. The default busybox version of hdparm
is broken, it reports the state of the disks always as "active/idle", even if the disk is in standby mode.
You can either build hdparm
from the source or install a new version using optware
. I you have updated your MBWE to firmware version 2.00.15 or newer, you probably don't have a c compiler anymore and your only option is to use the optware version.
I haven't tried the
optwareversion (8.5-1 as of writing) ofhdparmmyself, but other users have reported that it works just fine as long as you just remember to call it instead of the default one. Theoptwareversion doesn't overwrite the default one, so if you are callinghdparmdirectly without a path, you will most likely be calling the default one.
Instructions on using optware can be found in the wiki. Just note that if you are using the optware version, you'll have to replace the path to hdparm
in the beginning of the smart_spindown
script with the path to the optware
version of hdparm
.
And here's how to build it from source:
Make a directory for source files
# mkdir /tmp/src/ # cd /tmp/src/
Download and install hdparm
# wget http://garr.dl.sourceforge.net/sourceforge/hdparm/hdparm-7.7.tar.gz # tar -zxvf hdparm-7.7.tar.gz # cd hdparm-7.7 # make # make install
You can then get rid of the source files
# cd .. # rm -rf hdparm-7.7*
Do not play with
hdparmunless you really know what you are doing, you might end up with a damaged hard drive.
Get the smart_spindown
script, place it to /usr/sbin
and its access rights
# wget http://kyyhkynen.net/stuff/mybook/smart_spindown # mv smart_spindown /usr/sbin # chmod 0700 /usr/sbin/smart_spindown # chown root:root /usr/sbin/smart_spindown
Then you should tinker with the settings in the beginning of the smart_spindown
(see comments in the script for details). At this time just set the name(s) of your disk(s).
You might want to first test drive the script with the logging output echoed to stdout
. So don't touch the lines involving logging just yet.
Also, leave the WAITTIME
(= the amount of time in seconds of disk inactivity to wait before spinning the disks down) to a fairly small amount so you don't have to wait too long. 60 or 120 will do just fine, just set it to a longer period after you have tested that the script works. I have mine set to 300 (= 5 minutes).
#!/bin/bash
#
# smart_spindown
#
# Copyright (C) 2003 by Bart Samwel
#
# You may do with this file (and parts thereof) whatever you want, as long
# as my copyright notice is retained.
#
# Extended by joerk at gentoo-wiki.com
# Heavily modified to fit WD MyBook World external hard drives by kyyhkynen at gmail.com
# Last updated 2008/04/23
#
###################################################################
#
# Configuration
#
# Disk(s) to monitor.
# If you have only one disk, leave DISK2 empty
DISK1=sda
DISK2=sdb
# hdparm location. If you are using the optware version,
# you have to change this into /opt/sbin/hdparm
HDPARM=/sbin/hdparm
# The base "no reads" wait time (in seconds). This is multiplied by
# the backoff factor to determine the real "no reads" wait time.
WAITTIME=300
# The maximum "no reads" wait time (in seconds).
# This also limits the backoff factor: the backoff factor cannot increase
# above a value that makes the "no reads" wait time larger than MAXWAIT.
# Default is 1200 seconds.
MAXWAIT=1200
# Time (in seconds) between polls to see if the disk is active again.
# Default is 30 seconds.
POLLTIME=30
# Output levels. Level 2 is verbose, level 1 is normal output.
# Enable all levels you would like to see.
OUTLEVEL1=true
OUTLEVEL2=true
# Decide which output to use. Useful if run in daemon mode
# echo or logger
# output1 is logged to normal, output2 to debug syslog
# Is PID of this shell logged?
OUTPUT1=echo
OUTPUT2=echo
#OUTPUTTAG="$(basename -- $0)[$$]"
#OUTPUT1="logger -t $OUTPUTTAG -p user.notice --"
#OUTPUT2="logger -t $OUTPUTTAG -p user.debug --"
# Scripts to run whe the disk(s) are spinned up / down
# If you dont need this functionality, just leave them as they are.
# The existence of these files is checked before they are run
# Just make sure that the files are executable, if they exist
EXECUTE_ON_SPINUP=/usr/sbin/smart_spindown_onspinup
EXECUTE_ON_SPINDOWN=/usr/sbin/smart_spindown_onspindown
#
# End of configuration. You shouldn't have to change lines below this :)
#
#########################################################################
# Device name(s) for the disk(s).
DEVNAME1=/dev/$DISK1
[ "$DISK2" ] && DEVNAME2=/dev/$DISK2
[ -z "$DISK2" ] && DEVNAME2=
# Stats file: the file used to monitor the disk's read activity.
# The first entry in this stats file must represent the read activity.
STATSFILE1=/sys/block/$DISK1/stat
STATSFILE2=/sys/block/$DISK2/stat
# Multiplication factor for the backoff after a spinup, in percentages.
# Default is 300 = factor 3.
BACKOFF_INCREASE_PCT=400
# Multiplication factor for the backoff at every poll that shows that
# the disk is spun down. This determines how fast the backoff value
# decreases.
BACKOFF_DECREASE_PCT=98
# Enable this if you don't use laptop_mode. This will make the script
# sync before spinning down the disc. To make this work, you must
# ensure that:
# 1. /proc/sys/vm/dirty_expire_centisecs is set to a high value. You can
# use 60000 for 10 minutes.
# 2. /proc/sys/vm/dirty_writeback_centisecs is set to the same value.
# 3. Your ext3 filesystems are mounted with "commit=n", where n is the
# number of seconds between commit. Use 600 for 10 minutes.
NO_LAPTOP_MODE=true
#
# Let's go!
#
# Number of poll times that the disc was found to be spun down.
POLLSSPUNDOWN=0
# Number of spindowns performed
SPINDOWNS=0
# Number of times (*100) the WAITTIME of no-reads required before spindown
BACKOFF_FACTOR=100
# Stats: Total time the disk has been up.
UPTIME=0
# Total duration of last spun-down period.
LASTDOWNTIME=-1
# Total duration of the last spun-up period.
LASTUPTIME=0
# Duration of the last poll. Always equal to POLLTIME except the first
# time around.
LASTPOLLTIME=0
# Make sure the stuff we use is in the cache. I've seen it happen
# that the script spun the disk down, and then "sleep" wasn't in
# the cache and the disk spun right up again. :)
true
false
sleep 1
# Log the end of script execution
trap "$OUTPUT1 'Exiting.'" EXIT
if [ "$DISK2" ] ; then
$OUTLEVEL1 && ${OUTPUT1} "Monitoring spindown opportunities for disks $DISK1 and $DISK2." ;
else
$OUTLEVEL1 && ${OUTPUT1} "Monituring spindown opportunities for disk $DISK1." ;
fi ;
if ($OUTLEVEL1) ; then
$HDPARM -C $DEVNAME1 $DEVNAME2 |grep active >/dev/null
if [ "$?" == "0" ] ; then
[ "$DISK2" ] && ${OUTPUT1} "A drive is currently spun up." ;
[ -z "$DISK2" ] && ${OUTPUT1} "The drive is currently spun up." ;
else
[ "$DISK2" ] && ${OUTPUT1} "Both drives are currently spun down." ;
[ -z "$DISK2" ] && ${OUTPUT1} "The drive is currently spun down." ;
fi ;
fi
while [[ /sbin/true ]]; do
$HDPARM -C $DEVNAME1 $DEVNAME2 |grep active >/dev/null
if [ "$?" == "0" ] ; then
# the disks have spun up, run the spinup script
if [ -f $EXECUTE_ON_SPINUP ] ; then
$EXECUTE_ON_SPINUP ;
fi ;
THISWAIT=$(($WAITTIME*$BACKOFF_FACTOR/100)) ;
if [[ $THISWAIT -gt $MAXWAIT ]] ; then
THISWAIT=$MAXWAIT ;
fi ;
# Increase the backoff irrespective of whether we failed
# or not. The backoff should drop again by the lack of
# spinups afterwards.
BACKOFF_FACTOR=$(($BACKOFF_FACTOR*$BACKOFF_INCREASE_PCT/100)) ;
if [[ $(($BACKOFF_FACTOR*$WAITTIME/100)) -gt $MAXWAIT ]] ; then
BACKOFF_FACTOR=$(($MAXWAIT*100/$WAITTIME)) ;
fi ;
UPTIME=$(($UPTIME+$LASTPOLLTIME)) ;
LASTUPTIME=$(($LASTUPTIME+$LASTPOLLTIME)) ;
if [ "$LASTDOWNTIME" -ge "0" ] ; then
$OUTLEVEL1 && ${OUTPUT1} "A drive spun up after $LASTDOWNTIME seconds. Total time up/down: $UPTIME/$(($POLLSSPUNDOWN*$POLLTIME)) (avg sleep time $(($POLLSSPUNDOWN*$POLLTIME/$SPINDOWNS)))" ;
fi
PREVIOUS_READS_DISK1=-1 ;
PREVIOUS_READS_DISK2=-1 ;
NEXT_READS_DISK1=-1 ;
NEXT_READS_DISK2=-1 ;
NUM_EQUALS=0 ;
$OUTLEVEL2 && ${OUTPUT2} "Waiting for $THISWAIT seconds of read inactivity..." ;
PREVIOUS_READS_DISK1=`cat $STATSFILE1 |awk '{ print $1; }'` ;
[ "$DISK2" ] && PREVIOUS_READS_DISK2=`cat $STATSFILE2 |awk '{ print $1; }'` ;
while [[ $(($NUM_EQUALS*5)) -lt $THISWAIT ]]; do
sleep 5 ;
UPTIME=$(($UPTIME+5)) ;
LASTUPTIME=$(($LASTUPTIME+5)) ;
NEXT_READS_DISK1=`cat $STATSFILE1 |awk '{ print $1; }'` ;
[ "$DISK2" ] && NEXT_READS_DISK2=`cat $STATSFILE2 |awk '{ print $1; }'` ;
if [[ $PREVIOUS_READS_DISK1 -ne $NEXT_READS_DISK1 ]] ; then
NUM_EQUALS=0 ;
PREVIOUS_READS_DISK1=$NEXT_READS_DISK1 ;
$OUTLEVEL2 && ${OUTPUT2} "$DISK1 read, restarting..." ;
else
if [ "$DISK2" -a $PREVIOUS_READS_DISK2 -ne $NEXT_READS_DISK2 ] ; then
NUM_EQUALS=0 ;
PREVIOUS_READS_DISK2=$NEXT_READS_DISK2 ;
$OUTLEVEL2 && ${OUTPUT2} "$DISK2 read, restarting..." ;
else
NUM_EQUALS=$(($NUM_EQUALS+1)) ;
$OUTLEVEL2 && ${OUTPUT2} "Seconds of quiet: $(($NUM_EQUALS*5))" ;
fi
fi
done
# We are going the din the disk(s) down, run the spindown script
if [ -f $EXECUTE_ON_SPINDOWN ] ; then
$EXECUTE_ON_SPINDOWN ;
fi ;
# We've just had $THISWAIT seconds of read inactivity. Writes can be
# cached, reads always spin up the disk; the inactivity indicates
# that we're ready to go to sleep. Laptop mode will have synced all
# writes for us after the last read, so we don't have to explicitly
# sync.
if ( $NO_LAPTOP_MODE ) ; then
sync ;
fi ;
$HDPARM -q -y $DEVNAME1 $DEVNAME2 ;
SPINDOWNS=$(($SPINDOWNS+1)) ;
$OUTLEVEL1 && ${OUTPUT1} "Drive(s) spun down after $LASTUPTIME seconds (with $THISWAIT seconds of inactivity), total spin down count: ${SPINDOWNS}." ;
LASTUPTIME=0 ;
LASTDOWNTIME=0 ;
else
POLLSSPUNDOWN=$(($POLLSSPUNDOWN+1)) ;
if [[ $SPINDOWNS -eq 0 ]] ; then
SPINDOWNS=1 ;
fi
LASTDOWNTIME=$(($LASTDOWNTIME+$LASTPOLLTIME)) ;
BACKOFF_FACTOR=$(($BACKOFF_FACTOR*$BACKOFF_DECREASE_PCT/100)) ;
if [ $BACKOFF_FACTOR -lt 100 ] ; then
BACKOFF_FACTOR=100 ;
fi
fi ;
if ( $OUTLEVEL2 ) ; then
${OUTPUT2} "spindowns: $SPINDOWNS, time up/down: $UPTIME/$(($POLLSSPUNDOWN*$POLLTIME)), backoff $BACKOFF_FACTOR, down for $LASTDOWNTIME (avg $(($POLLSSPUNDOWN*$POLLTIME/$SPINDOWNS)))." ;
fi ;
sleep $POLLTIME ;
LASTPOLLTIME=$POLLTIME ;
done
Now let's test the script. First, set the kernel parameters so that the disk writes will be cached. Later on, the service startup script will take care of these, but now that we are only testing, we'll have to set them manually.
# echo 0 > /proc/sys/vm/dirty_expire_centisecs # echo 0 > /proc/sys/vm/dirty_writeback_centisecs # echo 95 > /proc/sys/vm/dirty_ratio # echo 10 > /proc/sys/vm/dirty_background_ratio # echo 5 > /proc/sys/vm/laptop_mode # echo 40 > /proc/sys/vm/swappiness
If you are interested in how each of the paremeters affect the system, here is a brief explanation.
Start the script, don't do anything else with the MBWE and watch the output. If you have the box nearby, you'll probably also hear the spin-downs and spin-ups.
# /usr/sbin/smart_spindown
Leave the script running for a couple of hours and you'll see if the disks spin up unintentionally. If they do, you'll probably have to reduce the disk usage somehow.
With my MBWE it seems to take some time after a reboot for the script to start really working. So wait patiently. Leave the script running for example for a night. Even if it keeps spinning up and down, one night doensn't probably kill your disks, right? :)
If the script works just fine, kill it by pressing CTRL+C
.
Now tweak the settings in the beginning of the script. Set the WAITTIME
to serve your needs; I have mine set to 300 (= 5 minutes). Then set the MAXWAIT
(= the amount of time in seconds of disk inactivity to wait at most, the time is changing dynamically between WAITTIME
and MAXWAIT
) accordingly.
Also, mind the logging options. If you would like to log the output to syslog
, uncomment the lines with logger
. To disable logging and thus reduce disk activity, set both LOGLEVEL1
and LOGLEVEL2
to false
.
If you wan't to have the log available, but don't want to use the syslog (you're going to disable it in my next guide anyway :), see my simple alternative logging script in the end of the alternative temperature monitor guide.
Next, we'll make the script start and stop automatically.
Download the script to start/stop the service, put it into /etc/init.d/
and set its access rights. Then make a symbolic link to stop the service when the MBWE is shutting down.
# wget http://kyyhkynen.net/stuff/mybook/S50smart_spindown # mv S50smart_spindown /etc/init.d # chmod 0700 /etc/init.d/S50smart_spindown # chown root:root /etc/init.d/S50smart_spindown # ln -s /etc/init.d/S50smart_spindown /etc/init.d/K50smart_spindown
The scripts in the directory
/etc/init.d/starting withS[number]are automatically run during system startup with parameterstart. The number defines the order the scripts are run. Likewise, scripts starting withK[number]are run when the system is shutting down with parameterstop.
This step is optional. If the script seems to work ok for you, you don't necessarily need to do this. You can try this also later if you find it necessary. Edit your fstab
with caution; you might mess your drives up by playing around with it.
Find this line in /etc/fstab
:
/dev/md3 /var ext3 defaults,noatime 0 2
Change it into this:
/dev/md3 /var ext3 defaults,noatime,commit=43200 0 2
Changing this line causes the filesystem on /var
(where log files and all frequently accessed stuff is located) to commit its journal to the disk only twice a day, thus reducing disk access.
Reboot your MBWE.
# reboot
You will lose your SSH connection during the boot, so reconnect to the MBWE.
The script should be now running. You can check it like this:
# ps -A | grep smart
You're done here. Enjoy your new sleepy MBWE.
If you wan't to tweak your settings without rebooting the whole device, just make your changes to the script and restart it by
# /etc/init.d/S50smart_spindown restart
If your disk(s) keep spinning up randomly, see Monitoring disk usage for further tips.
You probably noted the EXECUTE_ON_SPINUP
and EXECUTE_ON_SPINDOWN
variables in the configuration section of the script. You can use these to do stuff when the disk(s) are spinned up and down.
Just point the variables to shell scripts you want to be run on spinup/spindown (make sure the scripts are set executable).
For example, I have set it up to turn the front panel leds on and backup my RAM disk to disk whenever the disks are spun up and turn the leds off when the disks are spun down.
Example spin up script:
#!/bin/bash # back up the RAM disk /etc/init.d/S13ramdisk backup # turn on the front panel leds echo 255 > /sys/class/leds/wdc-leds:power/brightness echo 100 > /sys/class/leds/wdc-leds:fuel-gauge/brightness
Example spin down script:
#!/bin/bash # turn off the front panel leds by settings their brightness to zero echo 0 > /sys/class/leds/wdc-leds:power/brightness echo 0 > /sys/class/leds/wdc-leds:fuel-gauge/brightness