| #! /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 |
| |