--- /dev/null
+diff -Nbaur linux-2.6.1/Documentation/laptop-mode.txt linux-2.6.1-withlaptopmode/Documentation/laptop-mode.txt
+--- linux-2.6.1/Documentation/laptop-mode.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.1-withlaptopmode/Documentation/laptop-mode.txt 2004-01-10 10:47:14.000000000 +0100
+@@ -0,0 +1,475 @@
++How to conserve battery power using laptop-mode
++-----------------------------------------------
++
++Document Author: Bart Samwel (bart@samwel.tk)
++Date created: January 2, 2004
++
++Introduction
++------------
++
++Laptopmode is used to minimize the time that the hard disk needs to be spun up,
++to conserve battery power on laptops. It has been reported to cause significant
++power savings.
++
++Contents
++--------
++
++* Introduction
++* The short story
++* Caveats
++* The details
++* Tips & Tricks
++* Control script
++* ACPI integration
++* Monitoring tool
++
++
++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:
++
++# laptop_mode start
++
++Then set your harddisk spindown time to a relatively low value with hdparm:
++
++hdparm -S 4 /dev/hda
++
++The value -S 4 means 20 seconds idle time before spindown. Your harddisk will
++now only spin up when a disk cache miss occurs, or at least once every 10
++minutes to write back any pending changes.
++
++To stop laptop_mode, remount your filesystems with regular commit intervals
++(e.g., 5 seconds), and run "laptop_mode stop".
++
++
++Caveats
++-------
++
++* The downside of laptop mode is that you have a chance of losing up
++ to 10 minutes of work. If you cannot afford this, don't use it!
++
++* Most desktop hard drives have a very limited lifetime measured in spindown
++ cycles, typically about 50.000 times (it's usually listed on the spec sheet).
++ Check your drive's rating, and don't wear down your drive's lifetime if you
++ don't need to.
++
++* If you mount some of your ext3/reiserfs filesystems with the -n option, then
++ the control script will not be able to remount them correctly. You must set
++ DO_REMOUNTS=0 in the control script, otherwise it will remount them with the
++ wrong options -- or it will fail because it cannot write to /etc/mtab.
++
++* 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.
++
++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
++
++To increase the effectiveness of the laptop_mode strategy, the laptop_mode
++control script increases dirty_expire_centisecs and dirty_writeback_centisecs in
++/proc/sys/vm to about 10 minutes (by default), which means that pages that are
++dirtied are not forced to be written to disk as often. The control script also
++changes the dirty background ratio, so that background writeback of dirty pages
++is not done anymore. Combined with a higher commit value (also 10 minutes) for
++ext3 or ReiserFS filesystems (also done automatically by the control script),
++this results in concentration of disk activity in a small time interval which
++occurs only once every 10 minutes, or whenever the disk is forced to spin up by
++a cache miss. The disk can then be spun down in the periods of inactivity.
++
++If you want to find out which process caused the disk to spin up, you can
++gather information by setting the flag /proc/sys/vm/block_dump. When this flag
++is set, Linux reports all disk read and write operations that take place, and
++all block dirtyings done to files. This makes it possible to debug why a disk
++needs to spin up, and to increase battery life even more.
++
++If 10 minutes is too much or too little downtime for you, you can configure
++this downtime as follows. In the control script, set the MAX_AGE value to the
++maximum number of seconds of disk downtime that you would like. You should
++then set your filesystem's commit interval to the same value. The dirty ratio
++is also configurable from the control script.
++
++If you don't like the idea of the control script remounting your filesystems
++for you, you can change DO_REMOUNTS to 0 in the script.
++
++Thanks to Kiko Piris, the control script can be used to enable laptop mode on
++both the Linux 2.4 and 2.6 series.
++
++
++Tips & Tricks
++-------------
++
++* Bartek Kania reports getting up to 50 minutes of extra battery life (on top
++ of his regular 3 to 3.5 hours) using very aggressive power management (hdparm
++ -B1) and a spindown time of 5 seconds (hdparm -S1).
++
++* You can spin down the disk while playing MP3, by setting the disk readahead
++ to 8MB (hdparm -a 16384). Effectively, the disk will read a complete MP3 at
++ once, and will then spin down while the MP3 is playing. (Thanks to Bartek
++ Kania.)
++
++
++Control script
++--------------
++
++Please note that this control script works for the Linux 2.4 and 2.6 series.
++
++--------------------CONTROL SCRIPT BEGIN------------------------------------------
++#!/bin/sh
++
++# start or stop laptop_mode, best run by a power management daemon when
++# ac gets connected/disconnected from a laptop
++#
++# install as /sbin/laptop_mode
++#
++# Contributors to this script: Kiko Piris
++# Bart Samwel
++# Dax Kelson
++# Original Linux 2.4 version by: Jens Axboe
++
++parse_mount_opts () {
++ echo "$*" | \
++ sed 's/.*/,&,/g' | \
++ sed 's/,commit=[0-9]*,/,/g' | \
++ sed 's/,,*/,/g' | \
++ sed 's/^,//' | \
++ sed 's/,$//' | \
++ cat -
++}
++
++KLEVEL="$(uname -r | cut -c1-3)"
++case "$KLEVEL" in
++ "2.4")
++ true
++ ;;
++ "2.6")
++ true
++ ;;
++ *)
++ echo "Unhandled kernel level: $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
++
++# Allowed dirty ratio, in pct. should be put into a sysconfig file as well.
++DIRTY_RATIO=40
++
++# kernel default dirty buffer age
++DEF_AGE=30
++DEF_UPDATE=5
++DEF_DIRTY_BACKGROUND_RATIO=10
++DEF_DIRTY_RATIO=40
++
++
++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
++fi
++
++case "$1" in
++ start)
++ AGE=$((100*$MAX_AGE))
++ echo -n "Starting laptop_mode"
++ case "$KLEVEL" in
++ "2.4")
++ echo "1" > /proc/sys/vm/laptop_mode
++ echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush
++ ;;
++ "2.6")
++ echo "1" > /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
++ echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_background_ratio
++ ;;
++ esac
++ if [ $DO_REMOUNTS -eq 1 ]; then
++ cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
++ PARSEDOPTS="$(parse_mount_opts "$OPTS")"
++ case "$FST" in
++ "ext3") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;;
++ "reiserfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;;
++ "xfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;;
++ esac
++ done
++ fi
++ echo "."
++ ;;
++ stop)
++ U_AGE=$((100*$DEF_UPDATE))
++ B_AGE=$((100*$DEF_AGE))
++ echo -n "Stopping laptop_mode"
++ case "$KLEVEL" in
++ "2.4")
++ echo "0" > /proc/sys/vm/laptop_mode
++ echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush
++ ;;
++ "2.6")
++ echo "0" > /proc/sys/vm/laptop_mode
++ echo "$U_AGE" > /proc/sys/vm/dirty_writeback_centisecs
++ echo "$B_AGE" > /proc/sys/vm/dirty_expire_centisecs
++ echo "$DEF_DIRTY_RATIO" > /proc/sys/vm/dirty_ratio
++ echo "$DEF_DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio
++ ;;
++ esac
++ if [ $DO_REMOUNTS -eq 1 ]; then
++ cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
++ PARSEDOPTS="$(parse_mount_opts "$OPTS")"
++ case "$FST" in
++ "ext3") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;;
++ "reiserfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;;
++ "xfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;;
++ esac
++ done
++ fi
++ echo "."
++ ;;
++ *)
++ echo "Usage: $0 {start|stop}"
++ ;;
++
++esac
++
++exit 0
++--------------------CONTROL SCRIPT END--------------------------------------------
++
++
++ACPI integration
++----------------
++
++Dax Kelson submitted this so that the ACPI acpid daemon will
++kick off the laptop_mode script and run hdparm.
++
++---------------------------/etc/acpi/events/ac_adapter BEGIN-------------------------------------------
++event=ac_adapter
++action=/etc/acpi/actions/battery.sh
++---------------------------/etc/acpi/events/ac_adapter END-------------------------------------------
++
++---------------------------/etc/acpi/actions/battery.sh BEGIN-------------------------------------------
++#!/bin/sh
++
++# cpu throttling
++# cat /proc/acpi/processor/CPU0/throttling for more info
++ACAD_THR=0
++BATT_THR=2
++
++# spindown time for HD (man hdparm for valid values)
++# I prefer 2 hours for acad and 20 seconds for batt
++ACAD_HD=244
++BATT_HD=4
++
++# ac/battery event handler
++
++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
++ /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
++ /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
++ exit 0
++ ;;
++esac
++---------------------------/etc/acpi/actions/battery.sh END-------------------------------------------
++
++Monitoring tool
++---------------
++
++Bartek Kania submitted this, it can be used to measure how much time your disk
++spends spun up/down.
++
++---------------------------dslm.c BEGIN-------------------------------------------
++/*
++ * Simple Disk SLeep Monitor
++ * by Bartek Kania
++ * Licenced under the GPL
++ */
++#include <unistd.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <time.h>
++#include <string.h>
++#include <signal.h>
++#include <sys/ioctl.h>
++#include <linux/hdreg.h>
++
++#ifdef DEBUG
++#define D(x) x
++#else
++#define D(x)
++#endif
++
++int endit = 0;
++
++/* Check if the disk is in powersave-mode
++ * Most of the code is stolen from hdparm.
++ * 1 = active, 0 = standby/sleep, -1 = unknown */
++int check_powermode(int fd)
++{
++ unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
++ int state;
++
++ if (ioctl(fd, HDIO_DRIVE_CMD, &args)
++ && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
++ && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
++ if (errno != EIO || args[0] != 0 || args[1] != 0) {
++ state = -1; /* "unknown"; */
++ } else
++ state = 0; /* "sleeping"; */
++ } else {
++ state = (args[2] == 255) ? 1 : 0;
++ }
++ D(printf(" drive state is: %s\n", state));
++
++ return state;
++}
++
++char *state_name(int i)
++{
++ if (i == -1) return "unknown";
++ if (i == 0) return "sleeping";
++ if (i == 1) return "active";
++
++ return "internal error";
++}
++
++char *myctime(time_t time)
++{
++ char *ts = ctime(&time);
++ ts[strlen(ts) - 1] = 0;
++
++ return ts;
++}
++
++void measure(int fd)
++{
++ time_t start_time;
++ int last_state;
++ time_t last_time;
++ int curr_state;
++ time_t curr_time = 0;
++ time_t time_diff;
++ time_t active_time = 0;
++ time_t sleep_time = 0;
++ time_t unknown_time = 0;
++ time_t total_time = 0;
++ int changes = 0;
++ float tmp;
++
++ printf("Starting measurements\n");
++
++ last_state = check_powermode(fd);
++ start_time = last_time = time(0);
++ printf(" System is in state %s\n\n", state_name(last_state));
++
++ while(!endit) {
++ sleep(1);
++ curr_state = check_powermode(fd);
++
++ if (curr_state != last_state || endit) {
++ changes++;
++ curr_time = time(0);
++ time_diff = curr_time - last_time;
++
++ if (last_state == 1) active_time += time_diff;
++ else if (last_state == 0) sleep_time += time_diff;
++ else unknown_time += time_diff;
++
++ last_state = curr_state;
++ last_time = curr_time;
++
++ printf("%s: State-change to %s\n", myctime(curr_time),
++ state_name(curr_state));
++ }
++ }
++ changes--; /* Compensate for SIGINT */
++
++ total_time = time(0) - start_time;
++ printf("\nTotal running time: %lus\n", curr_time - start_time);
++ printf(" State changed %d times\n", changes);
++
++ tmp = (float)sleep_time / (float)total_time * 100;
++ printf(" Time in sleep state: %lus (%.2f%%)\n", sleep_time, tmp);
++ tmp = (float)active_time / (float)total_time * 100;
++ printf(" Time in active state: %lus (%.2f%%)\n", active_time, tmp);
++ tmp = (float)unknown_time / (float)total_time * 100;
++ printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp);
++}
++
++void ender(int s)
++{
++ endit = 1;
++}
++
++void usage()
++{
++ puts("usage: dslm [-w <time>] <disk>");
++ exit(0);
++}
++
++int main(int ac, char **av)
++{
++ int fd;
++ char *disk = 0;
++ int settle_time = 60;
++
++ /* Parse the simple command-line */
++ if (ac == 2)
++ disk = av[1];
++ else if (ac == 4) {
++ settle_time = atoi(av[2]);
++ disk = av[3];
++ } else
++ usage();
++
++ if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) {
++ printf("Can't open %s, because: %s\n", disk, strerror(errno));
++ exit(-1);
++ }
++
++ if (settle_time) {
++ printf("Waiting %d seconds for the system to settle down to "
++ "'normal'\n", settle_time);
++ sleep(settle_time);
++ } else
++ puts("Not waiting for system to settle down");
++
++ signal(SIGINT, ender);
++
++ measure(fd);
++
++ close(fd);
++
++ return 0;
++}
++---------------------------dslm.c END---------------------------------------------
+diff -Nbaur linux-2.6.1/drivers/block/ll_rw_blk.c linux-2.6.1-withlaptopmode/drivers/block/ll_rw_blk.c
+--- linux-2.6.1/drivers/block/ll_rw_blk.c 2004-01-09 20:29:05.000000000 +0100
++++ linux-2.6.1-withlaptopmode/drivers/block/ll_rw_blk.c 2004-01-09 20:29:39.000000000 +0100
+@@ -27,6 +27,7 @@
+ #include <linux/completion.h>
+ #include <linux/slab.h>
+ #include <linux/swap.h>
++#include <linux/writeback.h>
+
+ static void blk_unplug_work(void *data);
+ static void blk_unplug_timeout(unsigned long data);
+@@ -2302,6 +2303,15 @@
+ mod_page_state(pgpgout, count);
+ else
+ mod_page_state(pgpgin, count);
++
++ if (unlikely(block_dump)) {
++ char b[BDEVNAME_SIZE];
++ printk("%s(%d): %s block %Lu on %s\n",
++ current->comm, current->pid,
++ (rw & WRITE) ? "WRITE" : "READ",
++ (unsigned long long)bio->bi_sector, bdevname(bio->bi_bdev,b));
++ }
++
+ generic_make_request(bio);
+ return 1;
+ }
+@@ -2593,6 +2603,12 @@
+ disk_stat_add(disk, write_ticks, duration);
+ break;
+ case READ:
++ /*
++ * schedule the writeout of pending dirty data when the disk is idle.
++ * (postpone writeback until system is quiescent again.)
++ */
++ if (unlikely(laptop_mode))
++ disk_is_spun_up(1);
+ disk_stat_inc(disk, reads);
+ disk_stat_add(disk, read_ticks, duration);
+ break;
+diff -Nbaur linux-2.6.1/fs/buffer.c linux-2.6.1-withlaptopmode/fs/buffer.c
+--- linux-2.6.1/fs/buffer.c 2004-01-09 20:29:10.000000000 +0100
++++ linux-2.6.1-withlaptopmode/fs/buffer.c 2004-01-09 20:29:45.000000000 +0100
+@@ -855,10 +855,13 @@
+ struct buffer_head *bh = head;
+
+ do {
+- if (buffer_uptodate(bh))
++ if (buffer_uptodate(bh)) {
+ set_buffer_dirty(bh);
+- else
++ if (unlikely(block_dump))
++ printk("%s(%d): dirtied buffer\n", current->comm, current->pid);
++ } else {
+ buffer_error();
++ }
+ bh = bh->b_this_page;
+ } while (bh != head);
+ }
+diff -Nbaur linux-2.6.1/include/linux/sysctl.h linux-2.6.1-withlaptopmode/include/linux/sysctl.h
+--- linux-2.6.1/include/linux/sysctl.h 2004-01-09 20:29:10.000000000 +0100
++++ linux-2.6.1-withlaptopmode/include/linux/sysctl.h 2004-01-09 20:29:46.000000000 +0100
+@@ -154,6 +154,8 @@
+ VM_SWAPPINESS=19, /* Tendency to steal mapped memory */
+ VM_LOWER_ZONE_PROTECTION=20,/* Amount of protection of lower zones */
+ VM_MIN_FREE_KBYTES=21, /* Minimum free kilobytes to maintain */
++ VM_LAPTOP_MODE=22, /* vm laptop mode */
++ VM_BLOCK_DUMP=23, /* block dump mode */
+ };
+
+
+diff -Nbaur linux-2.6.1/include/linux/writeback.h linux-2.6.1-withlaptopmode/include/linux/writeback.h
+--- linux-2.6.1/include/linux/writeback.h 2003-12-24 05:19:46.000000000 +0100
++++ linux-2.6.1-withlaptopmode/include/linux/writeback.h 2004-01-04 12:10:58.000000000 +0100
+@@ -71,12 +71,15 @@
+ * mm/page-writeback.c
+ */
+ int wakeup_bdflush(long nr_pages);
++void disk_is_spun_up(int postpone_writeback);
+
+-/* These 5 are exported to sysctl. */
++/* These are exported to sysctl. */
+ extern int dirty_background_ratio;
+ extern int vm_dirty_ratio;
+ extern int dirty_writeback_centisecs;
+ extern int dirty_expire_centisecs;
++extern int block_dump;
++extern int laptop_mode;
+
+ struct ctl_table;
+ struct file;
+diff -Nbaur linux-2.6.1/kernel/sysctl.c linux-2.6.1-withlaptopmode/kernel/sysctl.c
+--- linux-2.6.1/kernel/sysctl.c 2003-12-24 05:19:46.000000000 +0100
++++ linux-2.6.1-withlaptopmode/kernel/sysctl.c 2003-12-24 06:24:53.000000000 +0100
+@@ -700,6 +700,26 @@
+ .strategy = &sysctl_intvec,
+ .extra1 = &zero,
+ },
++ {
++ .ctl_name = VM_LAPTOP_MODE,
++ .procname = "laptop_mode",
++ .data = &laptop_mode,
++ .maxlen = sizeof(laptop_mode),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ .strategy = &sysctl_intvec,
++ .extra1 = &zero,
++ },
++ {
++ .ctl_name = VM_BLOCK_DUMP,
++ .procname = "block_dump",
++ .data = &block_dump,
++ .maxlen = sizeof(block_dump),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ .strategy = &sysctl_intvec,
++ .extra1 = &zero,
++ },
+ { .ctl_name = 0 }
+ };
+
+diff -Nbaur linux-2.6.1/mm/page-writeback.c linux-2.6.1-withlaptopmode/mm/page-writeback.c
+--- linux-2.6.1/mm/page-writeback.c 2003-12-24 05:19:46.000000000 +0100
++++ linux-2.6.1-withlaptopmode/mm/page-writeback.c 2004-01-04 17:50:03.000000000 +0100
+@@ -28,6 +28,7 @@
+ #include <linux/smp.h>
+ #include <linux/sysctl.h>
+ #include <linux/cpu.h>
++#include <linux/quotaops.h>
+
+ /*
+ * The maximum number of pages to writeout in a single bdflush/kupdate
+@@ -81,6 +82,16 @@
+ */
+ int dirty_expire_centisecs = 30 * 100;
+
++/*
++ * Flag that makes the machine dump writes/reads and block dirtyings.
++ */
++int block_dump;
++
++/*
++ * Flag that puts the machine in "laptop mode".
++ */
++int laptop_mode;
++
+ /* End of sysctl-exported parameters */
+
+
+@@ -195,7 +206,18 @@
+ if (nr_reclaimable + ps.nr_writeback <= dirty_thresh)
+ dirty_exceeded = 0;
+
+- if (!writeback_in_progress(bdi) && nr_reclaimable > background_thresh)
++ if (unlikely(laptop_mode) && pages_written > 0) {
++ /*
++ * Schedule full writeout to happen soon. We don't postpone
++ * previously scheduled full writeouts, otherwise a writing
++ * process throttled by balance_dirty_pages will be able to
++ * postpone the full writeout indefinitely, keeping the disk
++ * spun up as a result.
++ */
++ disk_is_spun_up(0);
++ }
++
++ if (!unlikely(laptop_mode) && !writeback_in_progress(bdi) && nr_reclaimable > background_thresh)
+ pdflush_operation(background_writeout, 0);
+ }
+
+@@ -327,6 +349,8 @@
+ oldest_jif = jiffies - (dirty_expire_centisecs * HZ) / 100;
+ start_jif = jiffies;
+ next_jif = start_jif + (dirty_writeback_centisecs * HZ) / 100;
++ if (laptop_mode)
++ wbc.older_than_this = NULL;
+ nr_to_write = ps.nr_dirty + ps.nr_unstable +
+ (inodes_stat.nr_inodes - inodes_stat.nr_unused);
+ while (nr_to_write > 0) {
+@@ -343,6 +367,11 @@
+ }
+ if (time_before(next_jif, jiffies + HZ))
+ next_jif = jiffies + HZ;
++ if (laptop_mode) {
++ sync_inodes(0);
++ sync_filesystems(0);
++ DQUOT_SYNC(NULL);
++ }
+ if (dirty_writeback_centisecs)
+ mod_timer(&wb_timer, next_jif);
+ }
+@@ -363,6 +392,28 @@
+ return 0;
+ }
+
++static struct timer_list laptop_mode_wb_timer;
++
++static void laptop_mode_wb_timer_fn(unsigned long unused)
++{
++ mod_timer(&wb_timer, jiffies);
++}
++
++/*
++ * We've spun up the disk and we're in laptop mode: schedule writeback
++ * of all dirty data in 5 seconds.
++ *
++ * Laptop mode writeback will be delayed if it has previously been
++ * scheduled to occur within 5 seconds. That way, the writeback will
++ * only be triggered if the system is truly quiet again.
++ */
++void disk_is_spun_up(int postpone_writeback)
++{
++ if (postpone_writeback || !timer_pending(&laptop_mode_wb_timer))
++ mod_timer(&laptop_mode_wb_timer, jiffies + 5 * HZ);
++}
++
++
+ static void wb_timer_fn(unsigned long unused)
+ {
+ if (pdflush_operation(wb_kupdate, 0) < 0)
+@@ -434,6 +485,11 @@
+ wb_timer.data = 0;
+ wb_timer.function = wb_timer_fn;
+ add_timer(&wb_timer);
++
++ init_timer(&laptop_mode_wb_timer);
++ laptop_mode_wb_timer.data = 0;
++ laptop_mode_wb_timer.function = laptop_mode_wb_timer_fn;
++
+ set_ratelimit();
+ register_cpu_notifier(&ratelimit_nb);
+ }
+@@ -525,6 +581,8 @@
+ __mark_inode_dirty(mapping->host,
+ I_DIRTY_PAGES);
+ }
++ if (unlikely(block_dump))
++ printk("%s(%d): dirtied page\n", current->comm, current->pid);
+ }
+ return ret;
+ }