Rombobjörn

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Persson <Bjorn@Rombobjörn.se>2018-03-18 08:16:40 +0100
committerBjörn Persson <Bjorn@Rombobjörn.se>2018-03-18 08:16:40 +0100
commit69d241362d4e45e7ce52136122fb116a2a640933 (patch)
tree16a4faac7f446a9172b0287ed225569ae630d7c2
imported
-rwxr-xr-xsmart_spindown246
-rw-r--r--smart_spindown@.service16
2 files changed, 262 insertions, 0 deletions
diff --git a/smart_spindown b/smart_spindown
new file mode 100755
index 0000000..9f7a3cb
--- /dev/null
+++ b/smart_spindown
@@ -0,0 +1,246 @@
+#! /bin/bash
+#
+# smart_spindown rev. 1
+#
+# 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
+# modified for a mostly idle backup disk by B Persson
+#
+#
+# A command line parameter --conf=<pathname> is required. The specified file
+# will be sourced as shell code. It must set the variable DISK to the pathname
+# of the disk to be monitored. It is recommended to use one of the links under
+# /dev/disk.
+#
+# How it works: This program monitors the activity on a disk. If there
+# is no activity for a while, the disk is spun down. The time without
+# activity that is required is dynamic, using a backoff factor. When
+# the recent spun-down periods are relatively short, this means that the
+# machine might be busy with something, so the script tries to wait for
+# longer periods without activity before spinning down again. When spun-down
+# periods are long, the backoff factor is decreased, and the disk is spun
+# down after shorter periods without activity.
+
+
+#
+# Configuration
+#
+
+# Output levels. Level 2 is verbose, level 1 is normal output.
+# Enable all levels you would like to see.
+OUTLEVEL1=true
+OUTLEVEL2=false
+
+# 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 --"
+
+# Multiplication factor for the backoff after a spinup, in percentages.
+# Default is 300 = factor 3.
+BACKOFF_INCREASE_PCT=300
+
+# 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=96
+
+# The base inactivity wait time (in seconds). This is multiplied by
+# the backoff factor to determine the real inactivity wait time.
+WAITTIME=150
+
+# The maximum inactivity wait time (in seconds).
+# This also limits the backoff factor: the backoff factor cannot increase
+# above a value that makes the inactivity wait time larger than MAXWAIT.
+# Default is 120 seconds.
+MAXWAIT=1200
+
+# Time (in seconds) between polls to see if the disk is active again.
+# Default is 10 seconds.
+POLLTIME=10
+
+# Enable this if you don't use laptop_mode. This will make the script
+# sync before spinning down the disk.
+NO_LAPTOP_MODE=true
+
+
+#
+# Parse the command line:
+#
+
+if [[ $# -ne 1 ]] ; then
+ echo 'Exactly one parameter is required, namely --conf=<pathname>.' >&2
+ exit 1
+fi
+
+case "$1" in
+ --conf=*)
+ CONF="${1#*=}"
+ ;;
+ *)
+ echo 'unrecognized parameter' >&2
+ exit 1
+ ;;
+esac
+
+
+#
+# Read the configuration:
+#
+
+source "$CONF"
+if [[ $? -ne 0 ]] ; then
+ echo 'Reading the configuration file failed.' >&2
+ exit 1
+fi
+
+# Get the device name of the disk to monitor:
+DEVNAME=`realpath "$DISK"`
+if [[ $? -ne 0 ]] ; then
+ echo 'bad disk pathname' >&2
+ exit 1
+fi
+if [[ ! -b "$DEVNAME" ]] ; then
+ echo 'The disk pathname is not a block device.' >&2
+ exit 1
+fi
+
+# Stats file: the file used to monitor the disk's activity.
+STATSFILE=/sys/block/`basename "$DEVNAME"`/stat
+
+
+#
+# Let's go!
+#
+
+# Number of poll times that the disk was found to be spun down.
+POLLSSPUNDOWN=0
+
+# Number of spindowns performed
+SPINDOWNS=0
+
+# Number of times (*100) the WAITTIME of inactivity required before spindown
+BACKOFF_FACTOR=100
+
+# Stats: Total time the disk has been up.
+UPTIME=0
+
+# Total duration of last spun-down period.
+LASTDOWNTIME=0
+
+# 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
+
+# Terminate on request.
+trap 'exit 0' HUP INT TERM
+
+# Log the end of script execution.
+trap "$OUTPUT1 'Exiting.'" EXIT
+
+
+$OUTLEVEL1 && ${OUTPUT1} "Monitoring spindown opportunities for $DEVNAME."
+if ($OUTLEVEL1) ; then
+ hdparm -C $DEVNAME |grep active >/dev/null
+ if [ $? -eq 0 ] ; then
+ ${OUTPUT1} "$DEVNAME is currently spun up." ;
+ else
+ ${OUTPUT1} "$DEVNAME is currently spun down." ;
+ fi ;
+fi
+while [[ true ]]; do
+ hdparm -C $DEVNAME |grep active >/dev/null
+ if [ $? -eq 0 ] ; then
+ 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 ;
+ if ( $OUTLEVEL2 ) ; then
+ # UPTIME is used only for OUTLEVEL2 messages.
+ UPTIME=$(($UPTIME+$LASTPOLLTIME)) ;
+ fi ;
+ LASTUPTIME=$(($LASTUPTIME+$LASTPOLLTIME)) ;
+ if [ $LASTPOLLTIME -ne 0 ] ; then
+ $OUTLEVEL1 && ${OUTPUT1} "$DEVNAME spun up after $LASTDOWNTIME seconds." ;
+ fi
+ NUM_EQUALS=0 ;
+ $OUTLEVEL2 && ${OUTPUT2} "Waiting for $THISWAIT seconds of inactivity..." ;
+ PREVIOUS_STATS=`cat $STATSFILE` ;
+ while [[ $(($NUM_EQUALS*5)) -lt $THISWAIT ]]; do
+ sleep 5 ;
+ if ( $OUTLEVEL2 ) ; then
+ # UPTIME is used only for OUTLEVEL2 messages.
+ UPTIME=$(($UPTIME+5)) ;
+ fi ;
+ LASTUPTIME=$(($LASTUPTIME+5)) ;
+ NEXT_STATS=`cat $STATSFILE` ;
+ if [ "$PREVIOUS_STATS" != "$NEXT_STATS" ] ; then
+ NUM_EQUALS=0 ;
+ PREVIOUS_STATS="$NEXT_STATS"
+ $OUTLEVEL2 && ${OUTPUT2} "Restarting..." ;
+ else
+ NUM_EQUALS=$(($NUM_EQUALS+1)) ;
+ $OUTLEVEL2 && ${OUTPUT2} "Seconds of quiet: $(($NUM_EQUALS*5))" ;
+ fi
+ done
+ # We've just had $THISWAIT seconds of inactivity. 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 $DEVNAME ;
+ if ( $OUTLEVEL2 ) ; then
+ # SPINDOWNS is used only for OUTLEVEL2 messages.
+ SPINDOWNS=$(($SPINDOWNS+1)) ;
+ fi ;
+ $OUTLEVEL1 && ${OUTPUT1} "$DEVNAME spun down after $LASTUPTIME seconds (with $THISWAIT seconds of inactivity)." ;
+ LASTUPTIME=0 ;
+ LASTDOWNTIME=0 ;
+ else
+ if ( $OUTLEVEL2 ) ; then
+ # POLLSSPUNDOWN and SPINDOWNS are used only for OUTLEVEL2 messages.
+ POLLSSPUNDOWN=$(($POLLSSPUNDOWN+1)) ;
+ if [[ $SPINDOWNS -eq 0 ]] ; then
+ SPINDOWNS=1 ;
+ fi
+ 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
diff --git a/smart_spindown@.service b/smart_spindown@.service
new file mode 100644
index 0000000..909bf5c
--- /dev/null
+++ b/smart_spindown@.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=Smart Spindown for %i
+Wants=local-fs.target
+After=syslog.target
+
+[Service]
+Type=idle
+WorkingDirectory=/etc/smart_spindown
+StandardOutput=syslog
+SyslogIdentifier=smart_spindown/output
+SyslogLevel=err
+ExecStart=/usr/local/bin/smart_spindown --conf=/etc/smart_spindown/%i.conf
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target