]> git.pld-linux.org Git - packages/kernel.git/blame - laptop-mode-2.6.1-7.patch
- [2.4.2x, 2.6.x] don't recursively crash in die() on CHRP/PReP machines
[packages/kernel.git] / laptop-mode-2.6.1-7.patch
CommitLineData
3f65c653 1diff -Nbaur linux-2.6.1/Documentation/laptop-mode.txt linux-2.6.1-withlaptopmode/Documentation/laptop-mode.txt
2--- linux-2.6.1/Documentation/laptop-mode.txt 1970-01-01 01:00:00.000000000 +0100
3+++ linux-2.6.1-withlaptopmode/Documentation/laptop-mode.txt 2004-01-10 10:47:14.000000000 +0100
4@@ -0,0 +1,475 @@
5+How to conserve battery power using laptop-mode
6+-----------------------------------------------
7+
8+Document Author: Bart Samwel (bart@samwel.tk)
9+Date created: January 2, 2004
10+
11+Introduction
12+------------
13+
14+Laptopmode is used to minimize the time that the hard disk needs to be spun up,
15+to conserve battery power on laptops. It has been reported to cause significant
16+power savings.
17+
18+Contents
19+--------
20+
21+* Introduction
22+* The short story
23+* Caveats
24+* The details
25+* Tips & Tricks
26+* Control script
27+* ACPI integration
28+* Monitoring tool
29+
30+
31+The short story
32+---------------
33+
34+If you just want to use it, run the laptop_mode control script (which is included
35+at the end of this document) as follows:
36+
37+# laptop_mode start
38+
39+Then set your harddisk spindown time to a relatively low value with hdparm:
40+
41+hdparm -S 4 /dev/hda
42+
43+The value -S 4 means 20 seconds idle time before spindown. Your harddisk will
44+now only spin up when a disk cache miss occurs, or at least once every 10
45+minutes to write back any pending changes.
46+
47+To stop laptop_mode, remount your filesystems with regular commit intervals
48+(e.g., 5 seconds), and run "laptop_mode stop".
49+
50+
51+Caveats
52+-------
53+
54+* The downside of laptop mode is that you have a chance of losing up
55+ to 10 minutes of work. If you cannot afford this, don't use it!
56+
57+* Most desktop hard drives have a very limited lifetime measured in spindown
58+ cycles, typically about 50.000 times (it's usually listed on the spec sheet).
59+ Check your drive's rating, and don't wear down your drive's lifetime if you
60+ don't need to.
61+
62+* If you mount some of your ext3/reiserfs filesystems with the -n option, then
63+ the control script will not be able to remount them correctly. You must set
64+ DO_REMOUNTS=0 in the control script, otherwise it will remount them with the
65+ wrong options -- or it will fail because it cannot write to /etc/mtab.
66+
67+* If you have your filesystems listed as type "auto" in fstab, like I did, then
68+ the control script will not recognize them as filesystems that need remounting.
69+
70+The details
71+-----------
72+
73+Laptop-mode is controlled by the flag /proc/sys/vm/laptop_mode. When this
74+flag is set, any physical disk read operation (that might have caused the
75+hard disk to spin up) causes Linux to flush all dirty blocks. The result
76+of this is that after a disk has spun down, it will not be spun up anymore
77+to write dirty blocks, because those blocks had already been written
78+immediately after the most recent read operation
79+
80+To increase the effectiveness of the laptop_mode strategy, the laptop_mode
81+control script increases dirty_expire_centisecs and dirty_writeback_centisecs in
82+/proc/sys/vm to about 10 minutes (by default), which means that pages that are
83+dirtied are not forced to be written to disk as often. The control script also
84+changes the dirty background ratio, so that background writeback of dirty pages
85+is not done anymore. Combined with a higher commit value (also 10 minutes) for
86+ext3 or ReiserFS filesystems (also done automatically by the control script),
87+this results in concentration of disk activity in a small time interval which
88+occurs only once every 10 minutes, or whenever the disk is forced to spin up by
89+a cache miss. The disk can then be spun down in the periods of inactivity.
90+
91+If you want to find out which process caused the disk to spin up, you can
92+gather information by setting the flag /proc/sys/vm/block_dump. When this flag
93+is set, Linux reports all disk read and write operations that take place, and
94+all block dirtyings done to files. This makes it possible to debug why a disk
95+needs to spin up, and to increase battery life even more.
96+
97+If 10 minutes is too much or too little downtime for you, you can configure
98+this downtime as follows. In the control script, set the MAX_AGE value to the
99+maximum number of seconds of disk downtime that you would like. You should
100+then set your filesystem's commit interval to the same value. The dirty ratio
101+is also configurable from the control script.
102+
103+If you don't like the idea of the control script remounting your filesystems
104+for you, you can change DO_REMOUNTS to 0 in the script.
105+
106+Thanks to Kiko Piris, the control script can be used to enable laptop mode on
107+both the Linux 2.4 and 2.6 series.
108+
109+
110+Tips & Tricks
111+-------------
112+
113+* Bartek Kania reports getting up to 50 minutes of extra battery life (on top
114+ of his regular 3 to 3.5 hours) using very aggressive power management (hdparm
115+ -B1) and a spindown time of 5 seconds (hdparm -S1).
116+
117+* You can spin down the disk while playing MP3, by setting the disk readahead
118+ to 8MB (hdparm -a 16384). Effectively, the disk will read a complete MP3 at
119+ once, and will then spin down while the MP3 is playing. (Thanks to Bartek
120+ Kania.)
121+
122+
123+Control script
124+--------------
125+
126+Please note that this control script works for the Linux 2.4 and 2.6 series.
127+
128+--------------------CONTROL SCRIPT BEGIN------------------------------------------
129+#!/bin/sh
130+
131+# start or stop laptop_mode, best run by a power management daemon when
132+# ac gets connected/disconnected from a laptop
133+#
134+# install as /sbin/laptop_mode
135+#
136+# Contributors to this script: Kiko Piris
137+# Bart Samwel
138+# Dax Kelson
139+# Original Linux 2.4 version by: Jens Axboe
140+
141+parse_mount_opts () {
142+ echo "$*" | \
143+ sed 's/.*/,&,/g' | \
144+ sed 's/,commit=[0-9]*,/,/g' | \
145+ sed 's/,,*/,/g' | \
146+ sed 's/^,//' | \
147+ sed 's/,$//' | \
148+ cat -
149+}
150+
151+KLEVEL="$(uname -r | cut -c1-3)"
152+case "$KLEVEL" in
153+ "2.4")
154+ true
155+ ;;
156+ "2.6")
157+ true
158+ ;;
159+ *)
160+ echo "Unhandled kernel level: $KLEVEL ('uname -r' = '$(uname -r)')"
161+ exit 1
162+ ;;
163+esac
164+
165+# Shall we remount journaled fs. with appropiate commit interval? (1=yes)
166+DO_REMOUNTS=1
167+
168+# age time, in seconds. should be put into a sysconfig file
169+MAX_AGE=600
170+
171+# Allowed dirty ratio, in pct. should be put into a sysconfig file as well.
172+DIRTY_RATIO=40
173+
174+# kernel default dirty buffer age
175+DEF_AGE=30
176+DEF_UPDATE=5
177+DEF_DIRTY_BACKGROUND_RATIO=10
178+DEF_DIRTY_RATIO=40
179+
180+
181+if [ ! -e /proc/sys/vm/laptop_mode ]; then
182+ echo "Kernel is not patched with laptop_mode patch."
183+ exit 1
184+fi
185+
186+if [ ! -w /proc/sys/vm/laptop_mode ]; then
187+ echo "You do not have enough privileges to enable laptop_mode."
188+ exit 1
189+fi
190+
191+case "$1" in
192+ start)
193+ AGE=$((100*$MAX_AGE))
194+ echo -n "Starting laptop_mode"
195+ case "$KLEVEL" in
196+ "2.4")
197+ echo "1" > /proc/sys/vm/laptop_mode
198+ echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush
199+ ;;
200+ "2.6")
201+ echo "1" > /proc/sys/vm/laptop_mode
202+ echo "$AGE" > /proc/sys/vm/dirty_writeback_centisecs
203+ echo "$AGE" > /proc/sys/vm/dirty_expire_centisecs
204+ echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_ratio
205+ echo "$DIRTY_RATIO" > /proc/sys/vm/dirty_background_ratio
206+ ;;
207+ esac
208+ if [ $DO_REMOUNTS -eq 1 ]; then
209+ cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
210+ PARSEDOPTS="$(parse_mount_opts "$OPTS")"
211+ case "$FST" in
212+ "ext3") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;;
213+ "reiserfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;;
214+ "xfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE ;;
215+ esac
216+ done
217+ fi
218+ echo "."
219+ ;;
220+ stop)
221+ U_AGE=$((100*$DEF_UPDATE))
222+ B_AGE=$((100*$DEF_AGE))
223+ echo -n "Stopping laptop_mode"
224+ case "$KLEVEL" in
225+ "2.4")
226+ echo "0" > /proc/sys/vm/laptop_mode
227+ echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush
228+ ;;
229+ "2.6")
230+ echo "0" > /proc/sys/vm/laptop_mode
231+ echo "$U_AGE" > /proc/sys/vm/dirty_writeback_centisecs
232+ echo "$B_AGE" > /proc/sys/vm/dirty_expire_centisecs
233+ echo "$DEF_DIRTY_RATIO" > /proc/sys/vm/dirty_ratio
234+ echo "$DEF_DIRTY_BACKGROUND_RATIO" > /proc/sys/vm/dirty_background_ratio
235+ ;;
236+ esac
237+ if [ $DO_REMOUNTS -eq 1 ]; then
238+ cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
239+ PARSEDOPTS="$(parse_mount_opts "$OPTS")"
240+ case "$FST" in
241+ "ext3") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;;
242+ "reiserfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;;
243+ "xfs") mount $DEV -t $FST $MP -o remount,$PARSEDOPTS ;;
244+ esac
245+ done
246+ fi
247+ echo "."
248+ ;;
249+ *)
250+ echo "Usage: $0 {start|stop}"
251+ ;;
252+
253+esac
254+
255+exit 0
256+--------------------CONTROL SCRIPT END--------------------------------------------
257+
258+
259+ACPI integration
260+----------------
261+
262+Dax Kelson submitted this so that the ACPI acpid daemon will
263+kick off the laptop_mode script and run hdparm.
264+
265+---------------------------/etc/acpi/events/ac_adapter BEGIN-------------------------------------------
266+event=ac_adapter
267+action=/etc/acpi/actions/battery.sh
268+---------------------------/etc/acpi/events/ac_adapter END-------------------------------------------
269+
270+---------------------------/etc/acpi/actions/battery.sh BEGIN-------------------------------------------
271+#!/bin/sh
272+
273+# cpu throttling
274+# cat /proc/acpi/processor/CPU0/throttling for more info
275+ACAD_THR=0
276+BATT_THR=2
277+
278+# spindown time for HD (man hdparm for valid values)
279+# I prefer 2 hours for acad and 20 seconds for batt
280+ACAD_HD=244
281+BATT_HD=4
282+
283+# ac/battery event handler
284+
285+status=`awk '/^state: / { print $2 }' /proc/acpi/ac_adapter/AC/state`
286+
287+case $status in
288+ "on-line")
289+ echo "Setting HD spindown to 2 hours"
290+ /sbin/laptop-mode stop
291+ /sbin/hdparm -S $ACAD_HD /dev/hda > /dev/null 2>&1
292+ /sbin/hdparm -B 255 /dev/hda > /dev/null 2>&1
293+ #echo -n $ACAD_CPU:$ACAD_THR > /proc/acpi/processor/CPU0/limit
294+ exit 0
295+ ;;
296+ "off-line")
297+ echo "Setting HD spindown to 20 seconds"
298+ /sbin/laptop-mode start
299+ /sbin/hdparm -S $BATT_HD /dev/hda > /dev/null 2>&1
300+ /sbin/hdparm -B 1 /dev/hda > /dev/null 2>&1
301+ #echo -n $BATT_CPU:$BATT_THR > /proc/acpi/processor/CPU0/limit
302+ exit 0
303+ ;;
304+esac
305+---------------------------/etc/acpi/actions/battery.sh END-------------------------------------------
306+
307+Monitoring tool
308+---------------
309+
310+Bartek Kania submitted this, it can be used to measure how much time your disk
311+spends spun up/down.
312+
313+---------------------------dslm.c BEGIN-------------------------------------------
314+/*
315+ * Simple Disk SLeep Monitor
316+ * by Bartek Kania
317+ * Licenced under the GPL
318+ */
319+#include <unistd.h>
320+#include <stdlib.h>
321+#include <stdio.h>
322+#include <fcntl.h>
323+#include <errno.h>
324+#include <time.h>
325+#include <string.h>
326+#include <signal.h>
327+#include <sys/ioctl.h>
328+#include <linux/hdreg.h>
329+
330+#ifdef DEBUG
331+#define D(x) x
332+#else
333+#define D(x)
334+#endif
335+
336+int endit = 0;
337+
338+/* Check if the disk is in powersave-mode
339+ * Most of the code is stolen from hdparm.
340+ * 1 = active, 0 = standby/sleep, -1 = unknown */
341+int check_powermode(int fd)
342+{
343+ unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
344+ int state;
345+
346+ if (ioctl(fd, HDIO_DRIVE_CMD, &args)
347+ && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
348+ && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
349+ if (errno != EIO || args[0] != 0 || args[1] != 0) {
350+ state = -1; /* "unknown"; */
351+ } else
352+ state = 0; /* "sleeping"; */
353+ } else {
354+ state = (args[2] == 255) ? 1 : 0;
355+ }
356+ D(printf(" drive state is: %s\n", state));
357+
358+ return state;
359+}
360+
361+char *state_name(int i)
362+{
363+ if (i == -1) return "unknown";
364+ if (i == 0) return "sleeping";
365+ if (i == 1) return "active";
366+
367+ return "internal error";
368+}
369+
370+char *myctime(time_t time)
371+{
372+ char *ts = ctime(&time);
373+ ts[strlen(ts) - 1] = 0;
374+
375+ return ts;
376+}
377+
378+void measure(int fd)
379+{
380+ time_t start_time;
381+ int last_state;
382+ time_t last_time;
383+ int curr_state;
384+ time_t curr_time = 0;
385+ time_t time_diff;
386+ time_t active_time = 0;
387+ time_t sleep_time = 0;
388+ time_t unknown_time = 0;
389+ time_t total_time = 0;
390+ int changes = 0;
391+ float tmp;
392+
393+ printf("Starting measurements\n");
394+
395+ last_state = check_powermode(fd);
396+ start_time = last_time = time(0);
397+ printf(" System is in state %s\n\n", state_name(last_state));
398+
399+ while(!endit) {
400+ sleep(1);
401+ curr_state = check_powermode(fd);
402+
403+ if (curr_state != last_state || endit) {
404+ changes++;
405+ curr_time = time(0);
406+ time_diff = curr_time - last_time;
407+
408+ if (last_state == 1) active_time += time_diff;
409+ else if (last_state == 0) sleep_time += time_diff;
410+ else unknown_time += time_diff;
411+
412+ last_state = curr_state;
413+ last_time = curr_time;
414+
415+ printf("%s: State-change to %s\n", myctime(curr_time),
416+ state_name(curr_state));
417+ }
418+ }
419+ changes--; /* Compensate for SIGINT */
420+
421+ total_time = time(0) - start_time;
422+ printf("\nTotal running time: %lus\n", curr_time - start_time);
423+ printf(" State changed %d times\n", changes);
424+
425+ tmp = (float)sleep_time / (float)total_time * 100;
426+ printf(" Time in sleep state: %lus (%.2f%%)\n", sleep_time, tmp);
427+ tmp = (float)active_time / (float)total_time * 100;
428+ printf(" Time in active state: %lus (%.2f%%)\n", active_time, tmp);
429+ tmp = (float)unknown_time / (float)total_time * 100;
430+ printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp);
431+}
432+
433+void ender(int s)
434+{
435+ endit = 1;
436+}
437+
438+void usage()
439+{
440+ puts("usage: dslm [-w <time>] <disk>");
441+ exit(0);
442+}
443+
444+int main(int ac, char **av)
445+{
446+ int fd;
447+ char *disk = 0;
448+ int settle_time = 60;
449+
450+ /* Parse the simple command-line */
451+ if (ac == 2)
452+ disk = av[1];
453+ else if (ac == 4) {
454+ settle_time = atoi(av[2]);
455+ disk = av[3];
456+ } else
457+ usage();
458+
459+ if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) {
460+ printf("Can't open %s, because: %s\n", disk, strerror(errno));
461+ exit(-1);
462+ }
463+
464+ if (settle_time) {
465+ printf("Waiting %d seconds for the system to settle down to "
466+ "'normal'\n", settle_time);
467+ sleep(settle_time);
468+ } else
469+ puts("Not waiting for system to settle down");
470+
471+ signal(SIGINT, ender);
472+
473+ measure(fd);
474+
475+ close(fd);
476+
477+ return 0;
478+}
479+---------------------------dslm.c END---------------------------------------------
480diff -Nbaur linux-2.6.1/drivers/block/ll_rw_blk.c linux-2.6.1-withlaptopmode/drivers/block/ll_rw_blk.c
481--- linux-2.6.1/drivers/block/ll_rw_blk.c 2004-01-09 20:29:05.000000000 +0100
482+++ linux-2.6.1-withlaptopmode/drivers/block/ll_rw_blk.c 2004-01-09 20:29:39.000000000 +0100
483@@ -27,6 +27,7 @@
484 #include <linux/completion.h>
485 #include <linux/slab.h>
486 #include <linux/swap.h>
487+#include <linux/writeback.h>
488
489 static void blk_unplug_work(void *data);
490 static void blk_unplug_timeout(unsigned long data);
491@@ -2302,6 +2303,15 @@
492 mod_page_state(pgpgout, count);
493 else
494 mod_page_state(pgpgin, count);
495+
496+ if (unlikely(block_dump)) {
497+ char b[BDEVNAME_SIZE];
498+ printk("%s(%d): %s block %Lu on %s\n",
499+ current->comm, current->pid,
500+ (rw & WRITE) ? "WRITE" : "READ",
501+ (unsigned long long)bio->bi_sector, bdevname(bio->bi_bdev,b));
502+ }
503+
504 generic_make_request(bio);
505 return 1;
506 }
507@@ -2593,6 +2603,12 @@
508 disk_stat_add(disk, write_ticks, duration);
509 break;
510 case READ:
511+ /*
512+ * schedule the writeout of pending dirty data when the disk is idle.
513+ * (postpone writeback until system is quiescent again.)
514+ */
515+ if (unlikely(laptop_mode))
516+ disk_is_spun_up(1);
517 disk_stat_inc(disk, reads);
518 disk_stat_add(disk, read_ticks, duration);
519 break;
520diff -Nbaur linux-2.6.1/fs/buffer.c linux-2.6.1-withlaptopmode/fs/buffer.c
521--- linux-2.6.1/fs/buffer.c 2004-01-09 20:29:10.000000000 +0100
522+++ linux-2.6.1-withlaptopmode/fs/buffer.c 2004-01-09 20:29:45.000000000 +0100
523@@ -855,10 +855,13 @@
524 struct buffer_head *bh = head;
525
526 do {
527- if (buffer_uptodate(bh))
528+ if (buffer_uptodate(bh)) {
529 set_buffer_dirty(bh);
530- else
531+ if (unlikely(block_dump))
532+ printk("%s(%d): dirtied buffer\n", current->comm, current->pid);
533+ } else {
534 buffer_error();
535+ }
536 bh = bh->b_this_page;
537 } while (bh != head);
538 }
539diff -Nbaur linux-2.6.1/include/linux/sysctl.h linux-2.6.1-withlaptopmode/include/linux/sysctl.h
540--- linux-2.6.1/include/linux/sysctl.h 2004-01-09 20:29:10.000000000 +0100
541+++ linux-2.6.1-withlaptopmode/include/linux/sysctl.h 2004-01-09 20:29:46.000000000 +0100
542@@ -154,6 +154,8 @@
543 VM_SWAPPINESS=19, /* Tendency to steal mapped memory */
544 VM_LOWER_ZONE_PROTECTION=20,/* Amount of protection of lower zones */
545 VM_MIN_FREE_KBYTES=21, /* Minimum free kilobytes to maintain */
546+ VM_LAPTOP_MODE=22, /* vm laptop mode */
547+ VM_BLOCK_DUMP=23, /* block dump mode */
548 };
549
550
551diff -Nbaur linux-2.6.1/include/linux/writeback.h linux-2.6.1-withlaptopmode/include/linux/writeback.h
552--- linux-2.6.1/include/linux/writeback.h 2003-12-24 05:19:46.000000000 +0100
553+++ linux-2.6.1-withlaptopmode/include/linux/writeback.h 2004-01-04 12:10:58.000000000 +0100
554@@ -71,12 +71,15 @@
555 * mm/page-writeback.c
556 */
557 int wakeup_bdflush(long nr_pages);
558+void disk_is_spun_up(int postpone_writeback);
559
560-/* These 5 are exported to sysctl. */
561+/* These are exported to sysctl. */
562 extern int dirty_background_ratio;
563 extern int vm_dirty_ratio;
564 extern int dirty_writeback_centisecs;
565 extern int dirty_expire_centisecs;
566+extern int block_dump;
567+extern int laptop_mode;
568
569 struct ctl_table;
570 struct file;
571diff -Nbaur linux-2.6.1/kernel/sysctl.c linux-2.6.1-withlaptopmode/kernel/sysctl.c
572--- linux-2.6.1/kernel/sysctl.c 2003-12-24 05:19:46.000000000 +0100
573+++ linux-2.6.1-withlaptopmode/kernel/sysctl.c 2003-12-24 06:24:53.000000000 +0100
574@@ -700,6 +700,26 @@
575 .strategy = &sysctl_intvec,
576 .extra1 = &zero,
577 },
578+ {
579+ .ctl_name = VM_LAPTOP_MODE,
580+ .procname = "laptop_mode",
581+ .data = &laptop_mode,
582+ .maxlen = sizeof(laptop_mode),
583+ .mode = 0644,
584+ .proc_handler = &proc_dointvec,
585+ .strategy = &sysctl_intvec,
586+ .extra1 = &zero,
587+ },
588+ {
589+ .ctl_name = VM_BLOCK_DUMP,
590+ .procname = "block_dump",
591+ .data = &block_dump,
592+ .maxlen = sizeof(block_dump),
593+ .mode = 0644,
594+ .proc_handler = &proc_dointvec,
595+ .strategy = &sysctl_intvec,
596+ .extra1 = &zero,
597+ },
598 { .ctl_name = 0 }
599 };
600
601diff -Nbaur linux-2.6.1/mm/page-writeback.c linux-2.6.1-withlaptopmode/mm/page-writeback.c
602--- linux-2.6.1/mm/page-writeback.c 2003-12-24 05:19:46.000000000 +0100
603+++ linux-2.6.1-withlaptopmode/mm/page-writeback.c 2004-01-04 17:50:03.000000000 +0100
604@@ -28,6 +28,7 @@
605 #include <linux/smp.h>
606 #include <linux/sysctl.h>
607 #include <linux/cpu.h>
608+#include <linux/quotaops.h>
609
610 /*
611 * The maximum number of pages to writeout in a single bdflush/kupdate
612@@ -81,6 +82,16 @@
613 */
614 int dirty_expire_centisecs = 30 * 100;
615
616+/*
617+ * Flag that makes the machine dump writes/reads and block dirtyings.
618+ */
619+int block_dump;
620+
621+/*
622+ * Flag that puts the machine in "laptop mode".
623+ */
624+int laptop_mode;
625+
626 /* End of sysctl-exported parameters */
627
628
629@@ -195,7 +206,18 @@
630 if (nr_reclaimable + ps.nr_writeback <= dirty_thresh)
631 dirty_exceeded = 0;
632
633- if (!writeback_in_progress(bdi) && nr_reclaimable > background_thresh)
634+ if (unlikely(laptop_mode) && pages_written > 0) {
635+ /*
636+ * Schedule full writeout to happen soon. We don't postpone
637+ * previously scheduled full writeouts, otherwise a writing
638+ * process throttled by balance_dirty_pages will be able to
639+ * postpone the full writeout indefinitely, keeping the disk
640+ * spun up as a result.
641+ */
642+ disk_is_spun_up(0);
643+ }
644+
645+ if (!unlikely(laptop_mode) && !writeback_in_progress(bdi) && nr_reclaimable > background_thresh)
646 pdflush_operation(background_writeout, 0);
647 }
648
649@@ -327,6 +349,8 @@
650 oldest_jif = jiffies - (dirty_expire_centisecs * HZ) / 100;
651 start_jif = jiffies;
652 next_jif = start_jif + (dirty_writeback_centisecs * HZ) / 100;
653+ if (laptop_mode)
654+ wbc.older_than_this = NULL;
655 nr_to_write = ps.nr_dirty + ps.nr_unstable +
656 (inodes_stat.nr_inodes - inodes_stat.nr_unused);
657 while (nr_to_write > 0) {
658@@ -343,6 +367,11 @@
659 }
660 if (time_before(next_jif, jiffies + HZ))
661 next_jif = jiffies + HZ;
662+ if (laptop_mode) {
663+ sync_inodes(0);
664+ sync_filesystems(0);
665+ DQUOT_SYNC(NULL);
666+ }
667 if (dirty_writeback_centisecs)
668 mod_timer(&wb_timer, next_jif);
669 }
670@@ -363,6 +392,28 @@
671 return 0;
672 }
673
674+static struct timer_list laptop_mode_wb_timer;
675+
676+static void laptop_mode_wb_timer_fn(unsigned long unused)
677+{
678+ mod_timer(&wb_timer, jiffies);
679+}
680+
681+/*
682+ * We've spun up the disk and we're in laptop mode: schedule writeback
683+ * of all dirty data in 5 seconds.
684+ *
685+ * Laptop mode writeback will be delayed if it has previously been
686+ * scheduled to occur within 5 seconds. That way, the writeback will
687+ * only be triggered if the system is truly quiet again.
688+ */
689+void disk_is_spun_up(int postpone_writeback)
690+{
691+ if (postpone_writeback || !timer_pending(&laptop_mode_wb_timer))
692+ mod_timer(&laptop_mode_wb_timer, jiffies + 5 * HZ);
693+}
694+
695+
696 static void wb_timer_fn(unsigned long unused)
697 {
698 if (pdflush_operation(wb_kupdate, 0) < 0)
699@@ -434,6 +485,11 @@
700 wb_timer.data = 0;
701 wb_timer.function = wb_timer_fn;
702 add_timer(&wb_timer);
703+
704+ init_timer(&laptop_mode_wb_timer);
705+ laptop_mode_wb_timer.data = 0;
706+ laptop_mode_wb_timer.function = laptop_mode_wb_timer_fn;
707+
708 set_ratelimit();
709 register_cpu_notifier(&ratelimit_nb);
710 }
711@@ -525,6 +581,8 @@
712 __mark_inode_dirty(mapping->host,
713 I_DIRTY_PAGES);
714 }
715+ if (unlikely(block_dump))
716+ printk("%s(%d): dirtied page\n", current->comm, current->pid);
717 }
718 return ret;
719 }
This page took 0.234041 seconds and 4 git commands to generate.