This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / Documentation / laptop-mode.txt
index 9df8d26..d2b6ec2 100644 (file)
@@ -28,8 +28,9 @@ Contents
 The short story
 ---------------
 
-If you just want to use it, run the laptop_mode control script (which is included
-at the end of this document) as follows:
+To use laptop mode, you don't need to set any kernel configuration options
+or anything. You simply need to run the laptop_mode control script (which
+is included in this document) as follows:
 
 # laptop_mode start
 
@@ -69,23 +70,22 @@ Caveats
 * If you have your filesystems listed as type "auto" in fstab, like I did, then
   the control script will not recognize them as filesystems that need remounting.
 
-* If you have XFS, make SURE that you set the XFS_HZ value in the control script
-  correctly, to the value of HZ of your running kernel. Laptop mode will not
-  work correctly if it is set too low, and you may lose data if it is set too
-  high. The reason for this problem is that XFS does not export its sysctl
-  variables in centisecs (like most other subsystems do) but in "jiffies",
-  which is an internal kernel measure. Once this is fixed things will get better.
+* It has been reported that some versions of the mutt mail client use file access
+  times to determine whether a folder contains new mail. If you use mutt and
+  experience this, you must disable the noatime remounting in the control script
+  by setting DO_REMOUNT_NOATIME=0.
 
 
 The details
 -----------
 
-Laptop-mode is controlled by the flag /proc/sys/vm/laptop_mode. When this
-flag is set, any physical disk read operation (that might have caused the
-hard disk to spin up) causes Linux to flush all dirty blocks. The result
-of this is that after a disk has spun down, it will not be spun up anymore
-to write dirty blocks, because those blocks had already been written
-immediately after the most recent read operation
+Laptop-mode is controlled by the flag /proc/sys/vm/laptop_mode. This flag is
+present for all kernels that have the laptop mode patch, regardless of any
+configuration options. When the flag is set, any physical disk read operation
+(that might have caused the hard disk to spin up) causes Linux to flush all dirty
+blocks. The result of this is that after a disk has spun down, it will not be spun
+up anymore to write dirty blocks, because those blocks had already been written
+immediately after the most recent read operation.
 
 To increase the effectiveness of the laptop_mode strategy, the laptop_mode
 control script increases dirty_expire_centisecs and dirty_writeback_centisecs in
@@ -165,7 +165,7 @@ Control script
 Please note that this control script works for the Linux 2.4 and 2.6 series.
 
 --------------------CONTROL SCRIPT BEGIN------------------------------------------
-#! /bin/sh
+#!/bin/bash
 
 # start or stop laptop_mode, best run by a power management daemon when
 # ac gets connected/disconnected from a laptop
@@ -176,22 +176,91 @@ Please note that this control script works for the Linux 2.4 and 2.6 series.
 #                               Bart Samwel
 #                               Micha Feigin
 #                               Andrew Morton
+#                               Herve Eychenne
 #                               Dax Kelson
 #
 # Original Linux 2.4 version by: Jens Axboe
 
+#############################################################################
+
+# Age time, in seconds. should be put into a sysconfig file
+MAX_AGE=600
+
+# Read-ahead, in kilobytes
+READAHEAD=4096
+
+# Shall we remount journaled fs. with appropiate commit interval? (1=yes)
+DO_REMOUNTS=1
+
+# And shall we add the "noatime" option to that as well? (1=yes)
+DO_REMOUNT_NOATIME=1
+
+# Dirty synchronous ratio.  At this percentage of dirty pages the process which
+# calls write() does its own writeback
+DIRTY_RATIO=40
+
+#
+# Allowed dirty background ratio, in percent.  Once DIRTY_RATIO has been
+# exceeded, the kernel will wake pdflush which will then reduce the amount
+# of dirty memory to dirty_background_ratio.  Set this nice and low, so once
+# some writeout has commenced, we do a lot of it.
+#
+DIRTY_BACKGROUND_RATIO=5
+
+# kernel default dirty buffer age
+DEF_AGE=30
+DEF_UPDATE=5
+DEF_DIRTY_BACKGROUND_RATIO=10
+DEF_DIRTY_RATIO=40
+DEF_XFS_AGE_BUFFER=15
+DEF_XFS_SYNC_INTERVAL=30
+DEF_XFS_BUFD_INTERVAL=1
+
+# This must be adjusted manually to the value of HZ in the running kernel
+# on 2.4, until the XFS people change their 2.4 external interfaces to work in
+# centisecs. This can be automated, but it's a work in progress that still needs
+# some fixes. On 2.6 kernels, XFS uses USER_HZ instead of HZ for external
+# interfaces, and that is currently always set to 100. So you don't need to
+# change this on 2.6.
+XFS_HZ=100
+
+#############################################################################
+
+KLEVEL="$(uname -r |
+             {
+              IFS='.' read a b c
+              echo $a.$b
+            }
+)"
+case "$KLEVEL" in
+       "2.4"|"2.6")
+               ;;
+       *)
+               echo "Unhandled kernel version: $KLEVEL ('uname -r' = '$(uname -r)')" >&2
+               exit 1
+               ;;
+esac
+
+if [ ! -e /proc/sys/vm/laptop_mode ] ; then
+       echo "Kernel is not patched with laptop_mode patch." >&2
+       exit 1
+fi
+
+if [ ! -w /proc/sys/vm/laptop_mode ] ; then
+       echo "You do not have enough privileges to enable laptop_mode." >&2
+       exit 1
+fi
+
 # Remove an option (the first parameter) of the form option=<number> 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 -
+       echo ",$*," | sed               \
+        -e 's/,'"$OPT"'=[0-9]*,/,/g'   \
+        -e 's/,,*/,/g'                 \
+        -e 's/^,//'                    \
+        -e 's/,$//'
 }
 
 # Remove an option (the first parameter) without any arguments from
@@ -199,13 +268,11 @@ parse_mount_opts () {
 parse_nonumber_mount_opts () {
        OPT="$1"
        shift
-       echo "$*"                       | \
-       sed 's/.*/,&,/'                 | \
-       sed 's/,'"$OPT"',/,/g'          | \
-       sed 's/,,*/,/g'                 | \
-       sed 's/^,//'                    | \
-       sed 's/,$//'                    | \
-       cat -
+       echo ",$*," | sed               \
+        -e 's/,'"$OPT"',/,/g'          \
+        -e 's/,,*/,/g'                 \
+        -e 's/^,//'                    \
+        -e 's/,$//'
 }
 
 # Find out the state of a yes/no option (e.g. "atime"/"noatime") in
@@ -220,28 +287,26 @@ parse_nonumber_mount_opts () {
 # 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_DEV="$1"
+       OPT="$2"
+       DEF_OPT="$3"
+       shift 3
        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
+       FSTAB_OPTS="$(awk '$1 == "'$L_DEV'" { print $4 }' /etc/fstab)"
+       if echo "$FSTAB_OPTS" | grep "$OPT" > /dev/null ; then
                # option specified in fstab: extract the value and use it
-               if [ -z "$(echo "$FSTAB_OPTS" | grep "no$OPT")" ] ; then
+               if echo "$FSTAB_OPTS" | grep "no$OPT" > /dev/null ; then
+                       echo "$PARSEDOPTS1,no$OPT"
+               else
                        # no$OPT not found -- so we must have $OPT.
                        echo "$PARSEDOPTS1,$OPT"
-               else
-                       echo "$PARSEDOPTS1,no$OPT"
                fi
+       else
+               # option not specified in fstab -- choose the default.
+               echo "$PARSEDOPTS1,$DEF_OPT"
        fi
 }
 
@@ -258,81 +323,28 @@ parse_yesno_opts_wfstab () {
 # 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_DEV="$1"
+       OPT="$2"
+       shift 2
        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
+       FSTAB_OPTS="$(awk '$1 == "'$L_DEV'" { print $4 }' /etc/fstab)"
+       if echo "$FSTAB_OPTS" | grep "$OPT=" > /dev/null ; then
                # 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 -
+               echo ",$FSTAB_OPTS," | sed \
+                -e 's/.*,'"$OPT"'=//'  \
+                -e 's/,.*//'
+       else
+               # option not specified in fstab: set it to 0
+               echo "$PARSEDOPTS1,$OPT=0"
        fi
 }
 
-KLEVEL="$(uname -r | cut -c1-3)"
-case "$KLEVEL" in
-       "2.4"|"2.6")
-               true
-               ;;
-       *)
-               echo "Unhandled kernel version: $KLEVEL ('uname -r' = '$(uname -r)')"
-               exit 1
-               ;;
-esac
-
-# Shall we remount journaled fs. with appropiate commit interval? (1=yes)
-DO_REMOUNTS=1
-
-# age time, in seconds. should be put into a sysconfig file
-MAX_AGE=600
-
-# Dirty synchronous ratio.  At this percentage of dirty pages the process which
-# calls write() does its own writeback
-DIRTY_RATIO=40
-
-#
-# Allowed dirty background ratio, in percent.  Once DIRTY_RATIO has been
-# exceeded, the kernel will wake pdflush which will then reduce the amount
-# of dirty memory to dirty_background_ratio.  Set this nice and low, so once
-# some writeout has commenced, we do a lot of it.
-#
-DIRTY_BACKGROUND_RATIO=5
-
-READAHEAD=4096         # kilobytes
-
-# kernel default dirty buffer age
-DEF_AGE=30
-DEF_UPDATE=5
-DEF_DIRTY_BACKGROUND_RATIO=10
-DEF_DIRTY_RATIO=40
-DEF_XFS_AGE_BUFFER=15
-DEF_XFS_SYNC_INTERVAL=30
-
-# This must be adjusted manually to the value of HZ in the running kernel,
-# until the XFS people change their external interfaces to work in centisecs
-# like the rest of the external world. Unfortunately this cannot be automated. :(
-XFS_HZ=1000
 
-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
+if [ $DO_REMOUNT_NOATIME -eq 1 ] ; then
+       NOATIME_OPT=",noatime"
 fi
 
 case "$1" in
@@ -342,28 +354,38 @@ case "$1" in
                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 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 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
@@ -376,10 +398,10 @@ case "$1" in
                                case "$FST" in
                                        "ext3"|"reiserfs")
                                                PARSEDOPTS="$(parse_mount_opts commit "$OPTS")"
-                                               mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE,noatime
+                                               mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE$NOATIME_OPT
                                                ;;
                                        "xfs")
-                                               mount $DEV -t $FST $MP -o remount,$OPTS,noatime
+                                               mount $DEV -t $FST $MP -o remount,$OPTS$NOATIME_OPT
                                                ;;
                                esac
                                if [ -b $DEV ] ; then
@@ -393,11 +415,16 @@ case "$1" in
                U_AGE=$((100*$DEF_UPDATE))
                B_AGE=$((100*$DEF_AGE))
                echo -n "Stopping laptop_mode"
-               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
+               echo 0 > /proc/sys/vm/laptop_mode
+               if [ -f /proc/sys/fs/xfs/age_buffer -a ! -f /proc/sys/fs/xfs/lm_age_buffer ] ; then
+                       # These need to be restored, 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")
@@ -410,7 +437,7 @@ case "$1" in
                                echo "$DEF_DIRTY_BACKGROUND_RATIO"      > /proc/sys/vm/dirty_background_ratio
                                ;;
                esac
-               if [ $DO_REMOUNTS -eq 1 ]; then
+               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
@@ -432,13 +459,13 @@ case "$1" in
                echo "."
                ;;
        *)
-               echo "Usage: $0 {start|stop}"
+               echo "Usage: $0 {start|stop}" 2>&1
+               exit 1
                ;;
 
 esac
 
 exit 0
-
 --------------------CONTROL SCRIPT END--------------------------------------------
 
 
@@ -472,16 +499,16 @@ status=`awk '/^state: / { print $2 }' /proc/acpi/ac_adapter/AC/state`
 
 case $status in
         "on-line")
-                echo "Setting HD spindown to 2 hours"
-                /sbin/laptop-mode stop
+                echo "Setting HD spindown for AC mode."
+                /sbin/laptop_mode stop
                 /sbin/hdparm -S $ACAD_HD /dev/hda > /dev/null 2>&1
                 /sbin/hdparm -B 255 /dev/hda > /dev/null 2>&1
                 #echo -n $ACAD_CPU:$ACAD_THR > /proc/acpi/processor/CPU0/limit
                 exit 0
         ;;
         "off-line")
-                echo "Setting HD spindown to 20 seconds"
-                /sbin/laptop-mode start
+                echo "Setting HD spindown for battery mode."
+                /sbin/laptop_mode start
                 /sbin/hdparm -S $BATT_HD /dev/hda > /dev/null 2>&1
                 /sbin/hdparm -B 1 /dev/hda > /dev/null 2>&1
                 #echo -n $BATT_CPU:$BATT_THR > /proc/acpi/processor/CPU0/limit