blob: f8fe1f36a13134933b2fa0b3588f2213d367093a [file] [log] [blame]
#! /bin/sh
#
# Laptop mode tools module: core laptop mode functionality
#
# Remove an option (the first parameter) of the form option=<number> from
# a mount options string (the rest of the parameters).
remove_numeric_mount_option () {
OPT="$1"
shift
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
# a mount option string (the rest of the parameters).
remove_fixed_mount_option () {
OPT="$1"
shift
echo ",$*," | sed \
-e 's|,'"$OPT"',|,|g' \
-e 's/,,*/,/g' \
-e 's/^,//' \
-e 's/,$//'
}
# Find out the state of an atime option ("atime"/"noatime"/"relatime"/"norelatime")
# in a set of mount options, and use this state to replace the value of the
# option in another mount options string.
#
# Example:
# replace_atime_mount_option defaults,user=1000,atime defaults,noatime
#
# This yields "defaults,atime".
replace_atime_mount_option () {
REPLACEMENT_OPTS="$1"
OPTS="$2"
PARSEDOPTS="$(remove_fixed_mount_option atime $OPTS)"
PARSEDOPTS="$(remove_fixed_mount_option noatime $PARSEDOPTS)"
PARSEDOPTS="$(remove_fixed_mount_option relatime $PARSEDOPTS)"
PARSEDOPTS="$(remove_fixed_mount_option norelatime $PARSEDOPTS)"
case ",$REPLACEMENT_OPTS," in
*",relatime,"*)
echo "$PARSEDOPTS,relatime"
;;
*",noatime,"*)
echo "$PARSEDOPTS,noatime"
;;
*)
# Kind of strange: to go from relatime to atime, we have to
# explicitly specify norelatime.
echo "$PARSEDOPTS,atime,norelatime"
;;
esac
}
# Find out the state of a numbered option (e.g. "commit=NNN") in
# a set of options, and use this state to replace the
# value of the option in another mount options string.
#
# Example:
# replace_numeric_mount_option commit defaults,user=1000,commit=3 defaults,commit=7
#
# This example yields "defaults,commit=3".
replace_numeric_mount_option () {
OPT="$1"
DEF_OPT="$2"
REPLACEMENT_OPTS="$3"
OPTS="$4"
PARSEDOPTS="$(remove_numeric_mount_option $OPT $OPTS)"
if echo ",$REPLACEMENT_OPTS," | grep ",$OPT=[0123456789]+," > /dev/null ; then
echo -n "$PARSEDOPTS,$OPT="
echo ",$REPLACEMENT_OPTS," | sed \
-e 's/.*,'"$OPT"'=//' \
-e 's/,.*//'
else
# Option not present in REPLACEMENT_OPTS: use the default.
echo "$PARSEDOPTS,$DEF_OPT"
fi
}
deduce_fstype () {
MP="$1"
# My root filesystem unfortunately has type "unknown" in
# /etc/mtab. If we encounter "unknown", we try to get the
# type from fstab. This still might be wrong, in which
# case the code further down will issue a big warning.
sed 's/[[:space:]]*#.*$//' /etc/fstab |
while read FSTAB_DEV FSTAB_MP FSTAB_FST FSTAB_OPTS FSTAB_DUMP FSTAB_DUMP ; do
if [ "$FSTAB_MP" = "$MP" ]; then
echo "$FSTAB_FST"
exit 0
fi
done
}
#
# Set kernel setting, showing an error if this fails.
#
# Parameter 1: sysctl/proc path
# Parameter 2: the value
#
set_sysctl() {
log "VERBOSE" "Executing: echo $2 > $1"
if ! echo "$2" > "$1" ; then
log "MSG" "SETTING OF KERNEL PARAMETER FAILED: echo $2 \> $1"
fi
}
if [ "$CONTROL_READAHEAD" -ne 0 ] ; then
if /sbin/blockdev --help 2>&1 | grep -Fq -- '--setfra' ; then
READAHEAD_OPTION=--setfra
else
READAHEAD_OPTION=--setra
if [ "$KLEVEL" = "2.4" ] ; then
log "MSG" "Warning: Running a 2.4 kernel with blockdev that does not support --setfra."
log "MSG" "File system readahead will not function properly."
fi
fi
fi
if [ $CONTROL_NOATIME -eq 1 ] ; then
if [ "$KLEVEL" = "2.4" ] ; then
log "VERBOSE" "Relatime is not supported on 2.4 kernels. Using noatime instead"
USE_RELATIME=0
elif [ "$KLEVEL" = "2.6" -a "$KMINOR" -lt 23 ] ; then
log "VERBOSE" "Relatime is not supported on kernels before 2.6.23. Using noatime instead."
USE_RELATIME=0
fi
if [ "$USE_RELATIME" = 1 ] ; then
NOATIME_OPT=",relatime"
else
NOATIME_OPT=",noatime"
fi
fi
# Adjust kernel settings and mount options (but only if data loss
# sensitive features are active)
if [ "$ACTIVATE_WITH_POSSIBLE_DATA_LOSS" -eq 1 ] ; then
# Take MAX_LOST_WORK_SECONDS from LM_BATT_MAX_LOST_WORK_SECONDS or LM_AC_MAX_LOST_WORK_SECONDS_WITH_LM, depending on power state.
MAX_LOST_WORK_SECONDS=$LM_BATT_MAX_LOST_WORK_SECONDS
if [ $ON_AC -eq 1 ] ; then
MAX_LOST_WORK_SECONDS=$LM_AC_MAX_LOST_WORK_SECONDS
fi
AGE=$((100*$MAX_LOST_WORK_SECONDS))
XFS_AGE=$(($XFS_HZ*$MAX_LOST_WORK_SECONDS))
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.
log "VERBOSE" "Adjusting XFS kernel parameters for 2.4 and early 2.6 kernels."
set_sysctl /proc/sys/vm/pagebuf/lm_flush_age $XFS_AGE
set_sysctl /proc/sys/fs/xfs/lm_sync_interval $XFS_AGE
elif [ -f /proc/sys/fs/xfs/lm_age_buffer ] ; then
# (A couple of early 2.6 laptop mode patches had these.)
# This only needs to be set, not reset -- it is only used when
# laptop mode is enabled.
log "VERBOSE" "Adjusting XFS kernel parameters for early patched 2.6 kernels."
set_sysctl /proc/sys/fs/xfs/lm_age_buffer $XFS_AGE
set_sysctl /proc/sys/fs/xfs/lm_sync_interval $XFS_AGE
elif [ -f /proc/sys/fs/xfs/age_buffer ] ; then
# (2.6.6)
# But not for these -- they are also used in normal
# operation.
log "VERBOSE" "Adjusting XFS kernel parameters for 2.6.6 kernel."
set_sysctl /proc/sys/fs/xfs/age_buffer $XFS_AGE
set_sysctl /proc/sys/fs/xfs/sync_interval $XFS_AGE
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.
log "VERBOSE" "Adjusting XFS kernel parameters for >2.6.7 kernel."
set_sysctl /proc/sys/fs/xfs/age_buffer_centisecs $AGE
set_sysctl /proc/sys/fs/xfs/xfssyncd_centisecs $AGE
set_sysctl /proc/sys/fs/xfs/xfsbufd_centisecs 3000
fi
case "$KLEVEL" in
"2.4")
log "VERBOSE" "Adjusting 2.4 kernel parameters to enable laptop mode."
set_sysctl /proc/sys/vm/laptop_mode 1
set_sysctl /proc/sys/vm/bdflush "30 500 0 0 $AGE $AGE 60 20 0"
;;
"2.6")
log "VERBOSE" "Adjusting 2.6 kernel parameters to enable laptop mode."
set_sysctl /proc/sys/vm/laptop_mode "$LM_SECONDS_BEFORE_SYNC"
set_sysctl /proc/sys/vm/dirty_writeback_centisecs "$AGE"
set_sysctl /proc/sys/vm/dirty_expire_centisecs "$AGE"
set_sysctl /proc/sys/vm/dirty_ratio "$LM_DIRTY_RATIO"
set_sysctl /proc/sys/vm/dirty_background_ratio "$LM_DIRTY_BACKGROUND_RATIO"
;;
esac
if [ $CONTROL_MOUNT_OPTIONS -eq 1 ]; then
log "VERBOSE" "Remounting filesystems."
# The -r flag makes 'read' preserve backslashes read from
# mtab. Spaces are represented by \040 in mtab. (also UTF-8)
cat /etc/mtab | while read -r DEV MP FST OPTS DUMP PASS ; do
# Now expand the escapes, inside quotes to preserve the spaces :)
MP="$(echo $MP)"
case "$FST" in
rootfs|unionfs|tmpfs|squashfs|sysfs|usbfs|proc|devpts)
continue
;;
esac
DO=0
case " $PARTITIONS " in
*" $DEV "*)
DO=1
log "VERBOSE" "$DEV found in PARTITIONS."
;;
*)
log "VERBOSE" "$DEV not found in PARTITIONS."
;;
esac
case " $PARTITIONS " in
*" $MP "*)
DO=1
log "VERBOSE" "$MP found in PARTITIONS."
;;
*)
log "VERBOSE" "$MP not found in PARTITIONS."
esac
case " $PARTITIONS " in
*" auto "*)
log "VERBOSE" "Checking $DEV against HD because PARTITIONS contains \"auto\"."
for THISHD in $HD ; do
log "VERBOSE" " Considering $THISHD."
case " $DEV" in *"$THISHD"*)
DO=1
log "VERBOSE" " $DEV contains $THISHD, which is in HD, so we will remount it."
break
;;
esac
done
;;
esac
if [ "$DO" -ne 0 ] ; then
log "VERBOSE" "Original options: $OPTS"
if [ "$WAS_ACTIVE" -eq 0 ] ; then
# Coming from inactive state: save last known mount options for the device.
log "VERBOSE" "Updating /var/run/laptop-mode-tools/nolm-mountopts."
if [ -f /var/run/laptop-mode-tools/nolm-mountopts ] ; then
sed -i "s|^$DEV .*$||" /var/run/laptop-mode-tools/nolm-mountopts
fi
echo $DEV $OPTS >> /var/run/laptop-mode-tools/nolm-mountopts
else
log "VERBOSE" "Not updating /var/run/laptop-mode-tools/nolm-mountopts because laptop mode was already active."
fi
if [ "$FST" = 'unknown' ]; then
log "VERBOSE" "Deducing fstype for $MP."
FST=$(deduce_fstype $MP)
log "VERBOSE" "Deduced fstype for $MP as $FST."
fi
# Strip stuff like ext3,ext2 into just ext3.
log "VERBOSE" "Reducing file system type."
FST=${FST%%,*}
case "$FST" in
"ext3"|"reiserfs"|"ext4dev"|"ext4")
log "VERBOSE" "Removing commit mount option from original options."
PARSEDOPTS="$(remove_numeric_mount_option commit "$OPTS")"
log "VERBOSE" "Executing: mount $DEV $MP -t $FST -o remount,$PARSEDOPTS,commit=$MAX_LOST_WORK_SECONDS$NOATIME_OPT"
if (! mount $DEV "$MP" -t $FST -o remount,$PARSEDOPTS,commit=$MAX_LOST_WORK_SECONDS$NOATIME_OPT) ; then
if [ "$FST" = "ext3" -a "$MP" = "/" ] ; then
echo "BIG FAT WARNING: Your root filesystem mounted as ext3 seems to lack support for"
echo "the commit mount option. This usually means that your root filesystem is"
echo "mounted as ext2 because there is no ext3 support in the kernel at boot time,"
echo "usually because you compiled ext3 as a module and don't load it in an initrd."
echo "Note that on recent 2.6 kernels, /proc/mounts shows the correct fs type for"
echo "the device /dev/root. You can check your actual root filesystem mount type"
echo "there. To fix the problem, either make ext3 available at boot time by compiling"
echo "it statically into the kernel, or configure the correct filesystem type in"
echo "/etc/fstab."
fi
fi
;;
*)
log "VERBOSE" "Executing: mount $DEV $MP -t $FST -o remount,$OPTS$NOATIME_OPT"
mount $DEV "$MP" -t $FST -o remount,$OPTS$NOATIME_OPT
;;
esac
if [ -b $DEV -a "$CONTROL_READAHEAD" -ne 0 ] ; then
log "VERBOSE" "Executing: /sbin/blockdev $READAHEAD_OPTION $(($LM_READAHEAD * 2)) $DEV"
log "VERBOSE" "`/sbin/blockdev $READAHEAD_OPTION $(($LM_READAHEAD * 2)) $DEV 2>&1`"
fi
fi
done
fi
else
# DEACTIVATE w.r.t. kernel options and mount point settings
U_AGE=$((100*$DEF_UPDATE))
B_AGE=$((100*$DEF_MAX_AGE))
set_sysctl /proc/sys/vm/laptop_mode 0
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_*.
log "VERBOSE" "Restoring default XFS settings (pre-centisecs version)."
set_sysctl /proc/sys/fs/xfs/age_buffer $(($XFS_HZ*$DEF_XFS_AGE_BUFFER))
set_sysctl /proc/sys/fs/xfs/sync_interval $(($XFS_HZ*$DEF_XFS_SYNC_INTERVAL))
elif [ -f /proc/sys/fs/xfs/age_buffer_centisecs ] ; then
# These need to be restored as well.
log "VERBOSE" "Restoring default XFS settings."
set_sysctl /proc/sys/fs/xfs/age_buffer_centisecs $((100*$DEF_XFS_AGE_BUFFER))
set_sysctl /proc/sys/fs/xfs/xfssyncd_centisecs $((100*$DEF_XFS_SYNC_INTERVAL))
set_sysctl /proc/sys/fs/xfs/xfsbufd_centisecs $((100*$DEF_XFS_BUFD_INTERVAL))
fi
case "$KLEVEL" in
"2.4")
log "VERBOSE" "Adjusting 2.4 kernel parameters to disable laptop mode."
set_sysctl /proc/sys/vm/bdflush "30 500 0 0 $U_AGE $B_AGE 60 20 0"
;;
"2.6")
log "VERBOSE" "Adjusting 2.6 kernel parameters to disable laptop mode."
set_sysctl /proc/sys/vm/dirty_writeback_centisecs "$U_AGE"
set_sysctl /proc/sys/vm/dirty_expire_centisecs "$B_AGE"
set_sysctl /proc/sys/vm/dirty_ratio "$NOLM_DIRTY_RATIO"
set_sysctl /proc/sys/vm/dirty_background_ratio "$NOLM_DIRTY_BACKGROUND_RATIO"
;;
esac
if [ $CONTROL_MOUNT_OPTIONS -eq 1 ] ; then
log "VERBOSE" "Remounting filesystems."
# The -r flag makes 'read' preserve backslashes read from
# mtab. Spaces are represented by \040 in mtab. (also UTF-8)
cat /etc/mtab | while read -r DEV MP FST OPTS DUMP PASS ; do
# Now expand the escapes, inside quotes to preserve the spaces :)
MP="$(echo $MP)"
DO=0
case " $PARTITIONS " in
*" $DEV "*)
DO=1
log "VERBOSE" "$DEV found in PARTITIONS."
;;
*)
log "VERBOSE" "$DEV not found in PARTITIONS."
;;
esac
case " $PARTITIONS " in
*" $MP "*)
DO=1
log "VERBOSE" "$MP found in PARTITIONS."
;;
*)
log "VERBOSE" "$MP not found in PARTITIONS."
;;
esac
case " $PARTITIONS " in
*" auto "*)
log "VERBOSE" "Checking $DEV against HD because PARTITIONS contains \"auto\"."
for THISHD in $HD ; do
log "VERBOSE" " Considering $THISHD."
case " $DEV" in *"$THISHD"*)
DO=1
log "VERBOSE" " $DEV contains $THISHD, which is in HD, so we will remount it."
break
;;
esac
done
;;
esac
if [ "$DO" -ne 0 ] ; then
# Reset commit and atime options to defaults.
log "VERBOSE" "Original options: $OPTS"
if [ "$FST" = 'unknown' ]; then
log "VERBOSE" "Deducing fstype for $MP."
FST=$(deduce_fstype $MP)
log "VERBOSE" "Deduced fstype for $MP as $FST."
fi
# Strip stuff like ext3,ext2 into just ext3.
log "VERBOSE" "Reducing file system type."
FST=${FST%%,*}
# Retrieve original non-laptop mode mount options and restore them.
# If the file that stores them doesn't exist, then laptop mode
# has never been started.
if [ "$WAS_ACTIVE" -ne 0 -a -f /var/run/laptop-mode-tools/nolm-mountopts ] ; then
SAVED_OPTS=`grep "^$DEV " /var/run/laptop-mode-tools/nolm-mountopts`
SAVED_OPTS=${SAVED_OPTS#* } # trim device name
case "$FST" in
"ext3"|"reiserfs"|"ext4dev"|"ext4")
PARSEDOPTS="$(replace_numeric_mount_option commit commit=0 $SAVED_OPTS $OPTS)"
PARSEDOPTS="$(replace_atime_mount_option $SAVED_OPTS $PARSEDOPTS)"
log "VERBOSE" "Executing: mount $DEV $MP -t $FST -o remount,$PARSEDOPTS"
mount $DEV "$MP" -t $FST -o remount,$PARSEDOPTS
;;
*)
PARSEDOPTS="$(replace_atime_mount_option $SAVED_OPTS $OPTS)"
log "VERBOSE" "Executing: mount $DEV $MP -t $FST -o remount,$PARSEDOPTS"
mount $DEV "$MP" -t $FST -o remount,$PARSEDOPTS
;;
esac
else
log "VERBOSE" "No saved mount options, so apparently we never remounted this filesystem during this session."
log "VERBOSE" "Not remounting."
fi
if [ -b $DEV -a "$CONTROL_READAHEAD" -ne 0 ] ; then
log "VERBOSE" "Executing: /sbin/blockdev $READAHEAD_OPTION $(($NOLM_READAHEAD * 2)) $DEV"
log "VERBOSE" "`/sbin/blockdev $READAHEAD_OPTION $(($NOLM_READAHEAD * 2)) $DEV 2>&1`"
fi
fi
done
fi
fi