#!/sbin/runscript # Copyright 1999-2004 Gentoo Technologies, Inc. # Distributed under the terms of the GNU General Public License v2 # $Header: # start or stop laptop_mode, best run by a power management daemon when # ac gets connected/disconnected from a laptop # # Contributors to this script: Kiko Piris # Bart Samwel # Dax Kelson # Original Linux 2.4 version by: Jens Axboe # # Adapted to Gentoo Linux by Johannes Ballé # Updated by Dennis Nienhüser depend() { need localmount # make sure standby settings by hdparm init script are overridden use hdparm } # Remove an option (the first parameter) of the form option= from # a mount options string (the rest of the parameters). parse_mount_opts () { OPT="$1" shift echo "$*" | \ sed 's/.*/,&,/' | \ sed 's/,'"$OPT"'=[0-9]*,/,/g' | \ sed 's/,,*/,/g' | \ sed 's/^,//' | \ sed 's/,$//' | \ cat - } # Remove an option (the first parameter) without any arguments from # a mount option string (the rest of the parameters). parse_nonumber_mount_opts () { OPT="$1" shift echo "$*" | \ sed 's/.*/,&,/' | \ sed 's/,'"$OPT"',/,/g' | \ sed 's/,,*/,/g' | \ sed 's/^,//' | \ sed 's/,$//' | \ cat - } # Find out the state of a yes/no option (e.g. "atime"/"noatime") in # fstab for a given filesystem, and use this state to replace the # value of the option in another mount options string. The device # is the first argument, the option name the second, and the default # value the third. The remainder is the mount options string. # # Example: # parse_yesno_opts_wfstab /dev/hda1 atime atime defaults,noatime # # If fstab contains, say, "rw" for this filesystem, then the result # will be "defaults,atime". parse_yesno_opts_wfstab () { L_DEV=$1 shift OPT=$1 shift DEF_OPT=$1 shift L_OPTS="$*" PARSEDOPTS1="$(parse_nonumber_mount_opts $OPT $L_OPTS)" PARSEDOPTS1="$(parse_nonumber_mount_opts no$OPT $PARSEDOPTS1)" # Watch for a default atime in fstab FSTAB_OPTS="$(cat /etc/fstab | sed 's/ / /g' | grep ^\ *"$L_DEV " | awk '{ print $4 }')" if [ -z "$(echo "$FSTAB_OPTS" | grep "$OPT")" ] ; then # option not specified in fstab -- choose the default. echo "$PARSEDOPTS1,$DEF_OPT" else # option specified in fstab: extract the value and use it if [ -z "$(echo "$FSTAB_OPTS" | grep "no$OPT")" ] ; then # no$OPT not found -- so we must have $OPT. echo "$PARSEDOPTS1,$OPT" else echo "$PARSEDOPTS1,no$OPT" fi fi } # Find out the state of a numbered option (e.g. "commit=NNN") in # fstab for a given filesystem, and use this state to replace the # value of the option in another mount options string. The device # is the first argument, and the option name the second. The # remainder is the mount options string in which the replacement # must be done. # # Example: # parse_mount_opts_wfstab /dev/hda1 commit defaults,commit=7 # # If fstab contains, say, "commit=3,rw" for this filesystem, then the # result will be "rw,commit=3". parse_mount_opts_wfstab () { L_DEV=$1 shift OPT=$1 shift L_OPTS="$*" PARSEDOPTS1="$(parse_mount_opts $OPT $L_OPTS)" # Watch for a default commit in fstab FSTAB_OPTS="$(cat /etc/fstab | sed 's/ / /g' | grep ^\ *"$L_DEV " | awk '{ print $4 }')" if [ -z "$(echo "$FSTAB_OPTS" | grep "$OPT=")" ] ; then # option not specified in fstab: set it to 0 echo "$PARSEDOPTS1,$OPT=0" else # option specified in fstab: extract the value, and use it echo -n "$PARSEDOPTS1,$OPT=" echo "$FSTAB_OPTS" | \ sed 's/.*/,&,/' | \ sed 's/.*,'"$OPT"'=//' | \ sed 's/,.*//' | \ cat - fi } proc_save() { if ! cat /proc/sys/vm/$1 >/var/lib/laptop_mode/$1; then eerror "Could not save /proc/sys/vm/$1. Make sure /var/lib/laptop_mode exists." return 1 fi } proc_restore() { if [ -f /var/lib/laptop_mode/$1 ]; then cat /var/lib/laptop_mode/$1 >/proc/sys/vm/$1 rm -f /var/lib/laptop_mode/$1 fi } checkconfig() { KLEVEL=$( uname -r | ( IFS="." read a b c echo $a.$b ) ) case "$KLEVEL" in "2.4"|"2.6") true ;; *) echo "Unhandled kernel version: $KLEVEL ('uname -r' = '$(uname -r)')" exit 1 ;; esac if [ ! -e /proc/sys/vm/laptop_mode ]; then echo "Kernel is not patched with laptop_mode patch." exit 1 fi if [ ! -w /proc/sys/vm/laptop_mode ]; then echo "You do not have enough privileges to enable laptop_mode." exit 1 fi if [ $DO_REMOUNT_NOATIME -eq 1 ]; then NOATIME_OPT=",noatime" fi all_discs="" for i in `dir /dev/discs`; do if [ ! -n "`ls -la /dev/discs/${i} | grep scsi`" ]; then all_discs="${all_discs} `basename ${i}`" fi done } start() { checkconfig || return 1 ebegin "Starting laptop_mode" AGE=$((100*$MAX_AGE)) XFS_AGE=$(($XFS_HZ*$MAX_AGE)) echo -n "Starting laptop_mode" if [ -d /proc/sys/vm/pagebuf ] ; then # (For 2.4 and early 2.6.) # This only needs to be set, not reset -- it is only used when # laptop mode is enabled. echo $XFS_AGE > /proc/sys/vm/pagebuf/lm_flush_age echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval elif [ -f /proc/sys/fs/xfs/lm_age_buffer ] ; then # (A couple of early 2.6 laptop mode patches had these.) # The same goes for these. echo $XFS_AGE > /proc/sys/fs/xfs/lm_age_buffer echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval elif [ -f /proc/sys/fs/xfs/age_buffer ] ; then # (2.6.6) # But not for these -- they are also used in normal # operation. echo $XFS_AGE > /proc/sys/fs/xfs/age_buffer echo $XFS_AGE > /proc/sys/fs/xfs/sync_interval elif [ -f /proc/sys/fs/xfs/age_buffer_centisecs ] ; then # (2.6.7 upwards) # And not for these either. These are in centisecs, # not USER_HZ, so we have to use $AGE, not $XFS_AGE. echo $AGE > /proc/sys/fs/xfs/age_buffer_centisecs echo $AGE > /proc/sys/fs/xfs/xfssyncd_centisecs echo 3000 > /proc/sys/fs/xfs/xfsbufd_centisecs fi case "$KLEVEL" in "2.4") echo "1" > /proc/sys/vm/laptop_mode echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush ;; "2.6") echo "5" > /proc/sys/vm/laptop_mode echo "$AGE" > /proc/sys/vm/dirty_writeback_centisecs echo "$AGE" > /proc/sys/vm/dirty_expire_centisecs echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_ratio echo "$DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio ;; esac if [ $DO_REMOUNTS -eq 1 ]; then cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do PARSEDOPTS="$(parse_mount_opts "$OPTS")" case "$FST" in "ext3"|"reiserfs") PARSEDOPTS="$(parse_mount_opts commit "$OPTS")" mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE$NOATIME_OPT ;; "xfs") mount $DEV -t $FST $MP -o remount,$OPTS$NOATIME_OPT ;; esac if [ -b $DEV ] ; then blockdev --setra $(($READAHEAD * 2)) $DEV fi done fi for i in ${all_discs}; do if ! hdparm -S$HD_STANDBY /dev/discs/$i/disc &>/dev/null; then ewarn "Failed to set disc standby on $i" fi done eend 0 } stop() { checkconfig || return 1 ebegin "Stopping laptop_mode" U_AGE=$((100*$DEF_UPDATE)) B_AGE=$((100*$DEF_AGE)) echo "0" > /proc/sys/vm/laptop_mode if [ -f /proc/sys/fs/xfs/age_buffer ] && [ ! -f /proc/sys/fs/xfs/lm_age_buffer ] ; then # These need to be restored though, if there are no lm_*. echo "$(($XFS_HZ*$DEF_XFS_AGE_BUFFER))" > /proc/sys/fs/xfs/age_buffer echo "$(($XFS_HZ*$DEF_XFS_SYNC_INTERVAL))" > /proc/sys/fs/xfs/sync_interval elif [ -f /proc/sys/fs/xfs/age_buffer_centisecs ] ; then # These need to be restored as well. echo "$((100*$DEF_XFS_AGE_BUFFER))" > /proc/sys/fs/xfs/age_buffer_centisecs echo "$((100*$DEF_XFS_SYNC_INTERVAL))" > /proc/sys/fs/xfs/xfssyncd_centisecs echo "$((100*$DEF_XFS_BUFD_INTERVAL))" > /proc/sys/fs/xfs/xfsbufd_centisecs fi case "$KLEVEL" in "2.4") echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush ;; "2.6") echo "$U_AGE" > /proc/sys/vm/dirty_writeback_centisecs echo "$B_AGE" > /proc/sys/vm/dirty_expire_centisecs echo "$DEF_DIRTY_RATIO" > /proc/sys/vm/dirty_ratio echo "$DEF_DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio ;; esac if [ $DO_REMOUNTS -eq 1 ]; then cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do # Reset commit and atime options to defaults. case "$FST" in "ext3"|"reiserfs") PARSEDOPTS="$(parse_mount_opts_wfstab $DEV commit $OPTS)" PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $PARSEDOPTS)" mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;; "xfs") PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $OPTS)" mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;; esac if [ -b $DEV ] ; then blockdev --setra 256 $DEV fi done fi for i in ${all_discs}; do if ! hdparm -S0 /dev/discs/$i/disc &>/dev/null; then ewarn "Failed to set disc standby on $i" fi done eend 0 }