1 How to conserve battery power using laptop-mode
2 -----------------------------------------------
4 Document Author: Bart Samwel (bart@samwel.tk)
5 Date created: January 2, 2004
6 Last modified: April 3, 2004
11 Laptopmode is used to minimize the time that the hard disk needs to be spun up,
12 to conserve battery power on laptops. It has been reported to cause significant
31 To use laptop mode, you don't need to set any kernel configuration options
32 or anything. You simply need to run the laptop_mode control script (which
33 is included in this document) as follows:
37 Then set your harddisk spindown time to a relatively low value with hdparm:
41 The value -S 4 means 20 seconds idle time before spindown. Your harddisk will
42 now only spin up when a disk cache miss occurs, or at least once every 10
43 minutes to write back any pending changes.
45 To stop laptop_mode, run "laptop_mode stop".
51 * The downside of laptop mode is that you have a chance of losing up
52 to 10 minutes of work. If you cannot afford this, don't use it! It's
53 wise to turn OFF laptop mode when you're almost out of battery --
54 although this will make the battery run out faster, at least you'll
55 lose less work when it actually runs out. I'm still looking for someone
56 to submit instructions on how to turn off laptop mode when battery is low,
57 e.g., using ACPI events. I don't have a laptop myself, so if you do and
58 you care to contribute such instructions, please do.
60 * Most desktop hard drives have a very limited lifetime measured in spindown
61 cycles, typically about 50.000 times (it's usually listed on the spec sheet).
62 Check your drive's rating, and don't wear down your drive's lifetime if you
65 * If you mount some of your ext3/reiserfs filesystems with the -n option, then
66 the control script will not be able to remount them correctly. You must set
67 DO_REMOUNTS=0 in the control script, otherwise it will remount them with the
68 wrong options -- or it will fail because it cannot write to /etc/mtab.
70 * If you have your filesystems listed as type "auto" in fstab, like I did, then
71 the control script will not recognize them as filesystems that need remounting.
73 * It has been reported that some versions of the mutt mail client use file access
74 times to determine whether a folder contains new mail. If you use mutt and
75 experience this, you must disable the noatime remounting in the control script
76 by setting DO_REMOUNT_NOATIME=0.
82 Laptop-mode is controlled by the flag /proc/sys/vm/laptop_mode. This flag is
83 present for all kernels that have the laptop mode patch, regardless of any
84 configuration options. When the flag is set, any physical disk read operation
85 (that might have caused the hard disk to spin up) causes Linux to flush all dirty
86 blocks. The result of this is that after a disk has spun down, it will not be spun
87 up anymore to write dirty blocks, because those blocks had already been written
88 immediately after the most recent read operation.
90 To increase the effectiveness of the laptop_mode strategy, the laptop_mode
91 control script increases dirty_expire_centisecs and dirty_writeback_centisecs in
92 /proc/sys/vm to about 10 minutes (by default), which means that pages that are
93 dirtied are not forced to be written to disk as often. The control script also
94 changes the dirty background ratio, so that background writeback of dirty pages
95 is not done anymore. Combined with a higher commit value (also 10 minutes) for
96 ext3 or ReiserFS filesystems (also done automatically by the control script),
97 this results in concentration of disk activity in a small time interval which
98 occurs only once every 10 minutes, or whenever the disk is forced to spin up by
99 a cache miss. The disk can then be spun down in the periods of inactivity.
101 If you want to find out which process caused the disk to spin up, you can
102 gather information by setting the flag /proc/sys/vm/block_dump. When this flag
103 is set, Linux reports all disk read and write operations that take place, and
104 all block dirtyings done to files. This makes it possible to debug why a disk
105 needs to spin up, and to increase battery life even more. The output of
106 block_dump is written to the kernel output, and it can be retrieved using
107 "dmesg". When you use block_dump, you may want to turn off klogd, otherwise
108 the output of block_dump will be logged, causing disk activity that is not
111 If 10 minutes is too much or too little downtime for you, you can configure
112 this downtime as follows. In the control script, set the MAX_AGE value to the
113 maximum number of seconds of disk downtime that you would like. You should
114 then set your filesystem's commit interval to the same value. The dirty ratio
115 is also configurable from the control script.
117 If you don't like the idea of the control script remounting your filesystems
118 for you, you can change DO_REMOUNTS to 0 in the script.
120 Thanks to Kiko Piris, the control script can be used to enable laptop mode on
121 both the Linux 2.4 and 2.6 series.
127 * Bartek Kania reports getting up to 50 minutes of extra battery life (on top
128 of his regular 3 to 3.5 hours) using very aggressive power management (hdparm
129 -B1) and a spindown time of 5 seconds (hdparm -S1).
131 * You can spin down the disk while playing MP3, by setting the disk readahead
132 to 8MB (hdparm -a 16384). Effectively, the disk will read a complete MP3 at
133 once, and will then spin down while the MP3 is playing. (Thanks to Bartek
136 * Drew Scott Daniels observed: "I don't know why, but when I decrease the number
137 of colours that my display uses it consumes less battery power. I've seen
138 this on powerbooks too. I hope that this is a piece of information that
139 might be useful to the Laptop Mode patch or it's users."
141 * One thing which will cause disks to spin up is not-present application
142 and dynamic library text pages. The kernel will load program text off disk
143 on-demand, so each time you invoke an application feature for the first
144 time, the kernel needs to spin the disk up to go and fetch that part of the
147 So it is useful to increase the disk readahead parameter greatly, so that
148 the kernel will pull all of the executable's pages into memory on the first
151 The supplied script does this.
153 * In syslog.conf, you can prefix entries with a dash ``-'' to omit syncing the
154 file after every logging. When you're using laptop-mode and your disk doesn't
155 spin down, this is a likely culprit.
157 * Richard Atterer observed that laptop mode does not work well with noflushd
158 (http://noflushd.sourceforge.net/), it seems that noflushd prevents laptop-mode
159 from doing its thing.
165 Please note that this control script works for the Linux 2.4 and 2.6 series.
167 --------------------CONTROL SCRIPT BEGIN------------------------------------------
170 # start or stop laptop_mode, best run by a power management daemon when
171 # ac gets connected/disconnected from a laptop
173 # install as /sbin/laptop_mode
175 # Contributors to this script: Kiko Piris
182 # Original Linux 2.4 version by: Jens Axboe
184 #############################################################################
186 # Age time, in seconds. should be put into a sysconfig file
189 # Read-ahead, in kilobytes
192 # Shall we remount journaled fs. with appropiate commit interval? (1=yes)
195 # And shall we add the "noatime" option to that as well? (1=yes)
198 # Dirty synchronous ratio. At this percentage of dirty pages the process which
199 # calls write() does its own writeback
203 # Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been
204 # exceeded, the kernel will wake pdflush which will then reduce the amount
205 # of dirty memory to dirty_background_ratio. Set this nice and low, so once
206 # some writeout has commenced, we do a lot of it.
208 DIRTY_BACKGROUND_RATIO=5
210 # kernel default dirty buffer age
213 DEF_DIRTY_BACKGROUND_RATIO=10
215 DEF_XFS_AGE_BUFFER=15
216 DEF_XFS_SYNC_INTERVAL=30
217 DEF_XFS_BUFD_INTERVAL=1
219 # This must be adjusted manually to the value of HZ in the running kernel
220 # on 2.4, until the XFS people change their 2.4 external interfaces to work in
221 # centisecs. This can be automated, but it's a work in progress that still needs
222 # some fixes. On 2.6 kernels, XFS uses USER_HZ instead of HZ for external
223 # interfaces, and that is currently always set to 100. So you don't need to
224 # change this on 2.6.
227 #############################################################################
239 echo "Unhandled kernel version: $KLEVEL ('uname -r' = '$(uname -r)')" >&2
244 if [ ! -e /proc/sys/vm/laptop_mode ] ; then
245 echo "Kernel is not patched with laptop_mode patch." >&2
249 if [ ! -w /proc/sys/vm/laptop_mode ] ; then
250 echo "You do not have enough privileges to enable laptop_mode." >&2
254 # Remove an option (the first parameter) of the form option=<number> from
255 # a mount options string (the rest of the parameters).
256 parse_mount_opts () {
260 -e 's/,'"$OPT"'=[0-9]*,/,/g' \
266 # Remove an option (the first parameter) without any arguments from
267 # a mount option string (the rest of the parameters).
268 parse_nonumber_mount_opts () {
272 -e 's/,'"$OPT"',/,/g' \
278 # Find out the state of a yes/no option (e.g. "atime"/"noatime") in
279 # fstab for a given filesystem, and use this state to replace the
280 # value of the option in another mount options string. The device
281 # is the first argument, the option name the second, and the default
282 # value the third. The remainder is the mount options string.
285 # parse_yesno_opts_wfstab /dev/hda1 atime atime defaults,noatime
287 # If fstab contains, say, "rw" for this filesystem, then the result
288 # will be "defaults,atime".
289 parse_yesno_opts_wfstab () {
295 PARSEDOPTS1="$(parse_nonumber_mount_opts $OPT $L_OPTS)"
296 PARSEDOPTS1="$(parse_nonumber_mount_opts no$OPT $PARSEDOPTS1)"
297 # Watch for a default atime in fstab
298 FSTAB_OPTS="$(awk '$1 == "'$L_DEV'" { print $4 }' /etc/fstab)"
299 if echo "$FSTAB_OPTS" | grep "$OPT" > /dev/null ; then
300 # option specified in fstab: extract the value and use it
301 if echo "$FSTAB_OPTS" | grep "no$OPT" > /dev/null ; then
302 echo "$PARSEDOPTS1,no$OPT"
304 # no$OPT not found -- so we must have $OPT.
305 echo "$PARSEDOPTS1,$OPT"
308 # option not specified in fstab -- choose the default.
309 echo "$PARSEDOPTS1,$DEF_OPT"
313 # Find out the state of a numbered option (e.g. "commit=NNN") in
314 # fstab for a given filesystem, and use this state to replace the
315 # value of the option in another mount options string. The device
316 # is the first argument, and the option name the second. The
317 # remainder is the mount options string in which the replacement
321 # parse_mount_opts_wfstab /dev/hda1 commit defaults,commit=7
323 # If fstab contains, say, "commit=3,rw" for this filesystem, then the
324 # result will be "rw,commit=3".
325 parse_mount_opts_wfstab () {
330 PARSEDOPTS1="$(parse_mount_opts $OPT $L_OPTS)"
331 # Watch for a default commit in fstab
332 FSTAB_OPTS="$(awk '$1 == "'$L_DEV'" { print $4 }' /etc/fstab)"
333 if echo "$FSTAB_OPTS" | grep "$OPT=" > /dev/null ; then
334 # option specified in fstab: extract the value, and use it
335 echo -n "$PARSEDOPTS1,$OPT="
336 echo ",$FSTAB_OPTS," | sed \
337 -e 's/.*,'"$OPT"'=//' \
340 # option not specified in fstab: set it to 0
341 echo "$PARSEDOPTS1,$OPT=0"
346 if [ $DO_REMOUNT_NOATIME -eq 1 ] ; then
347 NOATIME_OPT=",noatime"
352 AGE=$((100*$MAX_AGE))
353 XFS_AGE=$(($XFS_HZ*$MAX_AGE))
354 echo -n "Starting laptop_mode"
356 if [ -d /proc/sys/vm/pagebuf ] ; then
357 # (For 2.4 and early 2.6.)
358 # This only needs to be set, not reset -- it is only used when
359 # laptop mode is enabled.
360 echo $XFS_AGE > /proc/sys/vm/pagebuf/lm_flush_age
361 echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval
362 elif [ -f /proc/sys/fs/xfs/lm_age_buffer ] ; then
363 # (A couple of early 2.6 laptop mode patches had these.)
364 # The same goes for these.
365 echo $XFS_AGE > /proc/sys/fs/xfs/lm_age_buffer
366 echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval
367 elif [ -f /proc/sys/fs/xfs/age_buffer ] ; then
369 # But not for these -- they are also used in normal
371 echo $XFS_AGE > /proc/sys/fs/xfs/age_buffer
372 echo $XFS_AGE > /proc/sys/fs/xfs/sync_interval
373 elif [ -f /proc/sys/fs/xfs/age_buffer_centisecs ] ; then
375 # And not for these either. These are in centisecs,
376 # not USER_HZ, so we have to use $AGE, not $XFS_AGE.
377 echo $AGE > /proc/sys/fs/xfs/age_buffer_centisecs
378 echo $AGE > /proc/sys/fs/xfs/xfssyncd_centisecs
379 echo 3000 > /proc/sys/fs/xfs/xfsbufd_centisecs
384 echo 1 > /proc/sys/vm/laptop_mode
385 echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush
388 echo 5 > /proc/sys/vm/laptop_mode
389 echo "$AGE" > /proc/sys/vm/dirty_writeback_centisecs
390 echo "$AGE" > /proc/sys/vm/dirty_expire_centisecs
391 echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_ratio
392 echo "$DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio
395 if [ $DO_REMOUNTS -eq 1 ]; then
396 cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
397 PARSEDOPTS="$(parse_mount_opts "$OPTS")"
400 PARSEDOPTS="$(parse_mount_opts commit "$OPTS")"
401 mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE$NOATIME_OPT
404 mount $DEV -t $FST $MP -o remount,$OPTS$NOATIME_OPT
407 if [ -b $DEV ] ; then
408 blockdev --setra $(($READAHEAD * 2)) $DEV
415 U_AGE=$((100*$DEF_UPDATE))
416 B_AGE=$((100*$DEF_AGE))
417 echo -n "Stopping laptop_mode"
418 echo 0 > /proc/sys/vm/laptop_mode
419 if [ -f /proc/sys/fs/xfs/age_buffer -a ! -f /proc/sys/fs/xfs/lm_age_buffer ] ; then
420 # These need to be restored, if there are no lm_*.
421 echo $(($XFS_HZ*$DEF_XFS_AGE_BUFFER)) > /proc/sys/fs/xfs/age_buffer
422 echo $(($XFS_HZ*$DEF_XFS_SYNC_INTERVAL)) > /proc/sys/fs/xfs/sync_interval
423 elif [ -f /proc/sys/fs/xfs/age_buffer_centisecs ] ; then
424 # These need to be restored as well.
425 echo $((100*$DEF_XFS_AGE_BUFFER)) > /proc/sys/fs/xfs/age_buffer_centisecs
426 echo $((100*$DEF_XFS_SYNC_INTERVAL)) > /proc/sys/fs/xfs/xfssyncd_centisecs
427 echo $((100*$DEF_XFS_BUFD_INTERVAL)) > /proc/sys/fs/xfs/xfsbufd_centisecs
431 echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush
434 echo "$U_AGE" > /proc/sys/vm/dirty_writeback_centisecs
435 echo "$B_AGE" > /proc/sys/vm/dirty_expire_centisecs
436 echo "$DEF_DIRTY_RATIO" > /proc/sys/vm/dirty_ratio
437 echo "$DEF_DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio
440 if [ $DO_REMOUNTS -eq 1 ] ; then
441 cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
442 # Reset commit and atime options to defaults.
445 PARSEDOPTS="$(parse_mount_opts_wfstab $DEV commit $OPTS)"
446 PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $PARSEDOPTS)"
447 mount $DEV -t $FST $MP -o remount,$PARSEDOPTS
450 PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $OPTS)"
451 mount $DEV -t $FST $MP -o remount,$PARSEDOPTS
454 if [ -b $DEV ] ; then
455 blockdev --setra 256 $DEV
462 echo "Usage: $0 {start|stop}" 2>&1
469 --------------------CONTROL SCRIPT END--------------------------------------------
475 Dax Kelson submitted this so that the ACPI acpid daemon will
476 kick off the laptop_mode script and run hdparm.
478 ---------------------------/etc/acpi/events/ac_adapter BEGIN-------------------------------------------
480 action=/etc/acpi/actions/battery.sh
481 ---------------------------/etc/acpi/events/ac_adapter END-------------------------------------------
483 ---------------------------/etc/acpi/actions/battery.sh BEGIN-------------------------------------------
487 # cat /proc/acpi/processor/CPU0/throttling for more info
491 # spindown time for HD (man hdparm for valid values)
492 # I prefer 2 hours for acad and 20 seconds for batt
496 # ac/battery event handler
498 status=`awk '/^state: / { print $2 }' /proc/acpi/ac_adapter/AC/state`
502 echo "Setting HD spindown for AC mode."
503 /sbin/laptop_mode stop
504 /sbin/hdparm -S $ACAD_HD /dev/hda > /dev/null 2>&1
505 /sbin/hdparm -B 255 /dev/hda > /dev/null 2>&1
506 #echo -n $ACAD_CPU:$ACAD_THR > /proc/acpi/processor/CPU0/limit
510 echo "Setting HD spindown for battery mode."
511 /sbin/laptop_mode start
512 /sbin/hdparm -S $BATT_HD /dev/hda > /dev/null 2>&1
513 /sbin/hdparm -B 1 /dev/hda > /dev/null 2>&1
514 #echo -n $BATT_CPU:$BATT_THR > /proc/acpi/processor/CPU0/limit
518 ---------------------------/etc/acpi/actions/battery.sh END-------------------------------------------
523 Bartek Kania submitted this, it can be used to measure how much time your disk
526 ---------------------------dslm.c BEGIN-------------------------------------------
528 * Simple Disk Sleep Monitor
530 * Licenced under the GPL
540 #include <sys/ioctl.h>
541 #include <linux/hdreg.h>
551 /* Check if the disk is in powersave-mode
552 * Most of the code is stolen from hdparm.
553 * 1 = active, 0 = standby/sleep, -1 = unknown */
554 int check_powermode(int fd)
556 unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
559 if (ioctl(fd, HDIO_DRIVE_CMD, &args)
560 && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
561 && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
562 if (errno != EIO || args[0] != 0 || args[1] != 0) {
563 state = -1; /* "unknown"; */
565 state = 0; /* "sleeping"; */
567 state = (args[2] == 255) ? 1 : 0;
569 D(printf(" drive state is: %d\n", state));
574 char *state_name(int i)
576 if (i == -1) return "unknown";
577 if (i == 0) return "sleeping";
578 if (i == 1) return "active";
580 return "internal error";
583 char *myctime(time_t time)
585 char *ts = ctime(&time);
586 ts[strlen(ts) - 1] = 0;
597 time_t curr_time = 0;
599 time_t active_time = 0;
600 time_t sleep_time = 0;
601 time_t unknown_time = 0;
602 time_t total_time = 0;
606 printf("Starting measurements\n");
608 last_state = check_powermode(fd);
609 start_time = last_time = time(0);
610 printf(" System is in state %s\n\n", state_name(last_state));
614 curr_state = check_powermode(fd);
616 if (curr_state != last_state || endit) {
619 time_diff = curr_time - last_time;
621 if (last_state == 1) active_time += time_diff;
622 else if (last_state == 0) sleep_time += time_diff;
623 else unknown_time += time_diff;
625 last_state = curr_state;
626 last_time = curr_time;
628 printf("%s: State-change to %s\n", myctime(curr_time),
629 state_name(curr_state));
632 changes--; /* Compensate for SIGINT */
634 total_time = time(0) - start_time;
635 printf("\nTotal running time: %lus\n", curr_time - start_time);
636 printf(" State changed %d times\n", changes);
638 tmp = (float)sleep_time / (float)total_time * 100;
639 printf(" Time in sleep state: %lus (%.2f%%)\n", sleep_time, tmp);
640 tmp = (float)active_time / (float)total_time * 100;
641 printf(" Time in active state: %lus (%.2f%%)\n", active_time, tmp);
642 tmp = (float)unknown_time / (float)total_time * 100;
643 printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp);
653 puts("usage: dslm [-w <time>] <disk>");
657 int main(int ac, char **av)
661 int settle_time = 60;
663 /* Parse the simple command-line */
667 settle_time = atoi(av[2]);
672 if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) {
673 printf("Can't open %s, because: %s\n", disk, strerror(errno));
678 printf("Waiting %d seconds for the system to settle down to "
679 "'normal'\n", settle_time);
682 puts("Not waiting for system to settle down");
684 signal(SIGINT, ender);
692 ---------------------------dslm.c END---------------------------------------------