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 If you just want to use it, run the laptop_mode control script (which is included
32 at the end of this document) as follows:
36 Then set your harddisk spindown time to a relatively low value with hdparm:
40 The value -S 4 means 20 seconds idle time before spindown. Your harddisk will
41 now only spin up when a disk cache miss occurs, or at least once every 10
42 minutes to write back any pending changes.
44 To stop laptop_mode, run "laptop_mode stop".
50 * The downside of laptop mode is that you have a chance of losing up
51 to 10 minutes of work. If you cannot afford this, don't use it! It's
52 wise to turn OFF laptop mode when you're almost out of battery --
53 although this will make the battery run out faster, at least you'll
54 lose less work when it actually runs out. I'm still looking for someone
55 to submit instructions on how to turn off laptop mode when battery is low,
56 e.g., using ACPI events. I don't have a laptop myself, so if you do and
57 you care to contribute such instructions, please do.
59 * Most desktop hard drives have a very limited lifetime measured in spindown
60 cycles, typically about 50.000 times (it's usually listed on the spec sheet).
61 Check your drive's rating, and don't wear down your drive's lifetime if you
64 * If you mount some of your ext3/reiserfs filesystems with the -n option, then
65 the control script will not be able to remount them correctly. You must set
66 DO_REMOUNTS=0 in the control script, otherwise it will remount them with the
67 wrong options -- or it will fail because it cannot write to /etc/mtab.
69 * If you have your filesystems listed as type "auto" in fstab, like I did, then
70 the control script will not recognize them as filesystems that need remounting.
72 * If you have XFS, make SURE that you set the XFS_HZ value in the control script
73 correctly, to the value of HZ of your running kernel. Laptop mode will not
74 work correctly if it is set too low, and you may lose data if it is set too
75 high. The reason for this problem is that XFS does not export its sysctl
76 variables in centisecs (like most other subsystems do) but in "jiffies",
77 which is an internal kernel measure. Once this is fixed things will get better.
83 Laptop-mode is controlled by the flag /proc/sys/vm/laptop_mode. When this
84 flag is set, any physical disk read operation (that might have caused the
85 hard disk to spin up) causes Linux to flush all dirty blocks. The result
86 of this is that after a disk has spun down, it will not be spun up anymore
87 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
181 # Original Linux 2.4 version by: Jens Axboe
183 # Remove an option (the first parameter) of the form option=<number> from
184 # a mount options string (the rest of the parameters).
185 parse_mount_opts () {
190 sed 's/,'"$OPT"'=[0-9]*,/,/g' | \
197 # Remove an option (the first parameter) without any arguments from
198 # a mount option string (the rest of the parameters).
199 parse_nonumber_mount_opts () {
204 sed 's/,'"$OPT"',/,/g' | \
211 # Find out the state of a yes/no option (e.g. "atime"/"noatime") in
212 # fstab for a given filesystem, and use this state to replace the
213 # value of the option in another mount options string. The device
214 # is the first argument, the option name the second, and the default
215 # value the third. The remainder is the mount options string.
218 # parse_yesno_opts_wfstab /dev/hda1 atime atime defaults,noatime
220 # If fstab contains, say, "rw" for this filesystem, then the result
221 # will be "defaults,atime".
222 parse_yesno_opts_wfstab () {
230 PARSEDOPTS1="$(parse_nonumber_mount_opts $OPT $L_OPTS)"
231 PARSEDOPTS1="$(parse_nonumber_mount_opts no$OPT $PARSEDOPTS1)"
232 # Watch for a default atime in fstab
233 FSTAB_OPTS="$(cat /etc/fstab | sed 's/ / /g' | grep ^\ *"$L_DEV " | awk '{ print $4 }')"
234 if [ -z "$(echo "$FSTAB_OPTS" | grep "$OPT")" ] ; then
235 # option not specified in fstab -- choose the default.
236 echo "$PARSEDOPTS1,$DEF_OPT"
238 # option specified in fstab: extract the value and use it
239 if [ -z "$(echo "$FSTAB_OPTS" | grep "no$OPT")" ] ; then
240 # no$OPT not found -- so we must have $OPT.
241 echo "$PARSEDOPTS1,$OPT"
243 echo "$PARSEDOPTS1,no$OPT"
248 # Find out the state of a numbered option (e.g. "commit=NNN") in
249 # fstab for a given filesystem, and use this state to replace the
250 # value of the option in another mount options string. The device
251 # is the first argument, and the option name the second. The
252 # remainder is the mount options string in which the replacement
256 # parse_mount_opts_wfstab /dev/hda1 commit defaults,commit=7
258 # If fstab contains, say, "commit=3,rw" for this filesystem, then the
259 # result will be "rw,commit=3".
260 parse_mount_opts_wfstab () {
267 PARSEDOPTS1="$(parse_mount_opts $OPT $L_OPTS)"
268 # Watch for a default commit in fstab
269 FSTAB_OPTS="$(cat /etc/fstab | sed 's/ / /g' | grep ^\ *"$L_DEV " | awk '{ print $4 }')"
270 if [ -z "$(echo "$FSTAB_OPTS" | grep "$OPT=")" ] ; then
271 # option not specified in fstab: set it to 0
272 echo "$PARSEDOPTS1,$OPT=0"
274 # option specified in fstab: extract the value, and use it
275 echo -n "$PARSEDOPTS1,$OPT="
276 echo "$FSTAB_OPTS" | \
278 sed 's/.*,'"$OPT"'=//' | \
284 KLEVEL="$(uname -r | cut -c1-3)"
290 echo "Unhandled kernel version: $KLEVEL ('uname -r' = '$(uname -r)')"
295 # Shall we remount journaled fs. with appropiate commit interval? (1=yes)
298 # age time, in seconds. should be put into a sysconfig file
301 # Dirty synchronous ratio. At this percentage of dirty pages the process which
302 # calls write() does its own writeback
306 # Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been
307 # exceeded, the kernel will wake pdflush which will then reduce the amount
308 # of dirty memory to dirty_background_ratio. Set this nice and low, so once
309 # some writeout has commenced, we do a lot of it.
311 DIRTY_BACKGROUND_RATIO=5
313 READAHEAD=4096 # kilobytes
315 # kernel default dirty buffer age
318 DEF_DIRTY_BACKGROUND_RATIO=10
320 DEF_XFS_AGE_BUFFER=15
321 DEF_XFS_SYNC_INTERVAL=30
323 # This must be adjusted manually to the value of HZ in the running kernel,
324 # until the XFS people change their external interfaces to work in centisecs
325 # like the rest of the external world. Unfortunately this cannot be automated. :(
328 if [ ! -e /proc/sys/vm/laptop_mode ]; then
329 echo "Kernel is not patched with laptop_mode patch."
333 if [ ! -w /proc/sys/vm/laptop_mode ]; then
334 echo "You do not have enough privileges to enable laptop_mode."
340 AGE=$((100*$MAX_AGE))
341 XFS_AGE=$(($XFS_HZ*$MAX_AGE))
342 echo -n "Starting laptop_mode"
344 if [ -d /proc/sys/vm/pagebuf ] ; then
345 # This only needs to be set, not reset -- it is only used when
346 # laptop mode is enabled.
347 echo $XFS_AGE > /proc/sys/vm/pagebuf/lm_flush_age
348 echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval
349 elif [ -f /proc/sys/fs/xfs/lm_age_buffer ] ; then
350 # The same goes for these.
351 echo $XFS_AGE > /proc/sys/fs/xfs/lm_age_buffer
352 echo $XFS_AGE > /proc/sys/fs/xfs/lm_sync_interval
353 elif [ -f /proc/sys/fs/xfs/age_buffer ] ; then
354 # But not for these -- they are also used in normal
356 echo $XFS_AGE > /proc/sys/fs/xfs/age_buffer
357 echo $XFS_AGE > /proc/sys/fs/xfs/sync_interval
362 echo "1" > /proc/sys/vm/laptop_mode
363 echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush
366 echo "5" > /proc/sys/vm/laptop_mode
367 echo "$AGE" > /proc/sys/vm/dirty_writeback_centisecs
368 echo "$AGE" > /proc/sys/vm/dirty_expire_centisecs
369 echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_ratio
370 echo "$DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio
373 if [ $DO_REMOUNTS -eq 1 ]; then
374 cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
375 PARSEDOPTS="$(parse_mount_opts "$OPTS")"
378 PARSEDOPTS="$(parse_mount_opts commit "$OPTS")"
379 mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE,noatime
382 mount $DEV -t $FST $MP -o remount,$OPTS,noatime
385 if [ -b $DEV ] ; then
386 blockdev --setra $(($READAHEAD * 2)) $DEV
393 U_AGE=$((100*$DEF_UPDATE))
394 B_AGE=$((100*$DEF_AGE))
395 echo -n "Stopping laptop_mode"
396 echo "0" > /proc/sys/vm/laptop_mode
397 if [ -f /proc/sys/fs/xfs/age_buffer ] && [ ! -f /proc/sys/fs/xfs/lm_age_buffer ] ; then
398 # These need to be restored though, if there are no lm_*.
399 echo "$(($XFS_HZ*$DEF_XFS_AGE_BUFFER))" > /proc/sys/fs/xfs/age_buffer
400 echo "$(($XFS_HZ*$DEF_XFS_SYNC_INTERVAL))" > /proc/sys/fs/xfs/sync_interval
404 echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush
407 echo "$U_AGE" > /proc/sys/vm/dirty_writeback_centisecs
408 echo "$B_AGE" > /proc/sys/vm/dirty_expire_centisecs
409 echo "$DEF_DIRTY_RATIO" > /proc/sys/vm/dirty_ratio
410 echo "$DEF_DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio
413 if [ $DO_REMOUNTS -eq 1 ]; then
414 cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
415 # Reset commit and atime options to defaults.
418 PARSEDOPTS="$(parse_mount_opts_wfstab $DEV commit $OPTS)"
419 PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $PARSEDOPTS)"
420 mount $DEV -t $FST $MP -o remount,$PARSEDOPTS
423 PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $OPTS)"
424 mount $DEV -t $FST $MP -o remount,$PARSEDOPTS
427 if [ -b $DEV ] ; then
428 blockdev --setra 256 $DEV
435 echo "Usage: $0 {start|stop}"
442 --------------------CONTROL SCRIPT END--------------------------------------------
448 Dax Kelson submitted this so that the ACPI acpid daemon will
449 kick off the laptop_mode script and run hdparm.
451 ---------------------------/etc/acpi/events/ac_adapter BEGIN-------------------------------------------
453 action=/etc/acpi/actions/battery.sh
454 ---------------------------/etc/acpi/events/ac_adapter END-------------------------------------------
456 ---------------------------/etc/acpi/actions/battery.sh BEGIN-------------------------------------------
460 # cat /proc/acpi/processor/CPU0/throttling for more info
464 # spindown time for HD (man hdparm for valid values)
465 # I prefer 2 hours for acad and 20 seconds for batt
469 # ac/battery event handler
471 status=`awk '/^state: / { print $2 }' /proc/acpi/ac_adapter/AC/state`
475 echo "Setting HD spindown to 2 hours"
476 /sbin/laptop-mode stop
477 /sbin/hdparm -S $ACAD_HD /dev/hda > /dev/null 2>&1
478 /sbin/hdparm -B 255 /dev/hda > /dev/null 2>&1
479 #echo -n $ACAD_CPU:$ACAD_THR > /proc/acpi/processor/CPU0/limit
483 echo "Setting HD spindown to 20 seconds"
484 /sbin/laptop-mode start
485 /sbin/hdparm -S $BATT_HD /dev/hda > /dev/null 2>&1
486 /sbin/hdparm -B 1 /dev/hda > /dev/null 2>&1
487 #echo -n $BATT_CPU:$BATT_THR > /proc/acpi/processor/CPU0/limit
491 ---------------------------/etc/acpi/actions/battery.sh END-------------------------------------------
496 Bartek Kania submitted this, it can be used to measure how much time your disk
499 ---------------------------dslm.c BEGIN-------------------------------------------
501 * Simple Disk Sleep Monitor
503 * Licenced under the GPL
513 #include <sys/ioctl.h>
514 #include <linux/hdreg.h>
524 /* Check if the disk is in powersave-mode
525 * Most of the code is stolen from hdparm.
526 * 1 = active, 0 = standby/sleep, -1 = unknown */
527 int check_powermode(int fd)
529 unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
532 if (ioctl(fd, HDIO_DRIVE_CMD, &args)
533 && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
534 && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
535 if (errno != EIO || args[0] != 0 || args[1] != 0) {
536 state = -1; /* "unknown"; */
538 state = 0; /* "sleeping"; */
540 state = (args[2] == 255) ? 1 : 0;
542 D(printf(" drive state is: %d\n", state));
547 char *state_name(int i)
549 if (i == -1) return "unknown";
550 if (i == 0) return "sleeping";
551 if (i == 1) return "active";
553 return "internal error";
556 char *myctime(time_t time)
558 char *ts = ctime(&time);
559 ts[strlen(ts) - 1] = 0;
570 time_t curr_time = 0;
572 time_t active_time = 0;
573 time_t sleep_time = 0;
574 time_t unknown_time = 0;
575 time_t total_time = 0;
579 printf("Starting measurements\n");
581 last_state = check_powermode(fd);
582 start_time = last_time = time(0);
583 printf(" System is in state %s\n\n", state_name(last_state));
587 curr_state = check_powermode(fd);
589 if (curr_state != last_state || endit) {
592 time_diff = curr_time - last_time;
594 if (last_state == 1) active_time += time_diff;
595 else if (last_state == 0) sleep_time += time_diff;
596 else unknown_time += time_diff;
598 last_state = curr_state;
599 last_time = curr_time;
601 printf("%s: State-change to %s\n", myctime(curr_time),
602 state_name(curr_state));
605 changes--; /* Compensate for SIGINT */
607 total_time = time(0) - start_time;
608 printf("\nTotal running time: %lus\n", curr_time - start_time);
609 printf(" State changed %d times\n", changes);
611 tmp = (float)sleep_time / (float)total_time * 100;
612 printf(" Time in sleep state: %lus (%.2f%%)\n", sleep_time, tmp);
613 tmp = (float)active_time / (float)total_time * 100;
614 printf(" Time in active state: %lus (%.2f%%)\n", active_time, tmp);
615 tmp = (float)unknown_time / (float)total_time * 100;
616 printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp);
626 puts("usage: dslm [-w <time>] <disk>");
630 int main(int ac, char **av)
634 int settle_time = 60;
636 /* Parse the simple command-line */
640 settle_time = atoi(av[2]);
645 if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) {
646 printf("Can't open %s, because: %s\n", disk, strerror(errno));
651 printf("Waiting %d seconds for the system to settle down to "
652 "'normal'\n", settle_time);
655 puts("Not waiting for system to settle down");
657 signal(SIGINT, ender);
665 ---------------------------dslm.c END---------------------------------------------