laptop mode, #2
Subsystems (4)
PATCH 1/1 laptop mode, #2
Hi,
Made a few tweaks and adjustments:
- If block_dump is set, also dump who is marking a page/buffer as dirty. akpm recommended this.
- Don't touch default bdflush parameters (see script)
That's about it. I've gotten several mails who really like the patch and that it really adds a non-significant amount of extra battery time. I consider the patch final at this point.
Patch is against 2.4.21-rc2 (ish)
Signature · 1 line
Jens Axboe
#!/bin/sh
#
# start of stop laptop mode, best run by a power management daemon when # ac gets connected/disconnected from a laptop # # FIXME: assumes HZ == 100
# age time, in seconds. should be put into a sysconfig file MAX_AGE=600
# kernel default dirty buffer age DEF_AGE=30 DEF_UPDATE=5
if [ ! -w /proc/sys/vm/laptop_mode ]; then
echo "Kernel is not patched with laptop_mode patch" exit 1
fi
case "$1" in
start)
AGE=$((100*$MAX_AGE))
echo -n "Starting laptop mode"
echo "1" > /proc/sys/vm/laptop_mode
echo "30 500 0 0 $AGE $AGE 60 20 0" > /proc/sys/vm/bdflush
echo "."
;;
stop)
U_AGE=$((100*$DEF_UPDATE))
B_AGE=$((100*$DEF_AGE))
echo -n "Stopping laptop mode"
echo "0" > /proc/sys/vm/laptop_mode
echo "30 500 0 0 $U_AGE $B_AGE 60 20 0" > /proc/sys/vm/bdflush
echo "."
;;
*)
echo "$0 {start|stop}"
;;esac
exit 0
# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas:
# ChangeSet 1.1209 -> 1.1210
# include/linux/sysctl.h 1.23 -> 1.24
# drivers/block/ll_rw_blk.c 1.44 -> 1.45
# kernel/sysctl.c 1.19 -> 1.20 # include/linux/fs.h 1.74 -> 1.75 # fs/jbd/transaction.c 1.13 -> 1.14 # mm/filemap.c 1.76 -> 1.77 # fs/buffer.c 1.82 -> 1.83
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/05/16 [email protected] 1.1210
# laptop mode #2 # -------------------------------------------- # diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c --- a/drivers/block/ll_rw_blk.c Fri May 16 13:32:56 2003 +++ b/drivers/block/ll_rw_blk.c Fri May 16 13:32:56 2003 @@ -121,6 +121,10 @@ unsigned long blk_max_low_pfn, blk_max_pfn; int blk_nohighio = 0;
+int block_dump = 0;
+
+static struct timer_list writeback_timer;
+
static inline int get_max_sectors(kdev_t dev)
{
if (!max_sectors[MAJOR(dev)])
@@ -1207,6 +1211,9 @@
kstat.pgpgin += count;
break;
}
+
+ if (block_dump)
+ printk("%s: %s block %lu/%u on %s\n", current->comm, rw == WRITE ? "WRITE" : "READ", bh->b_rsector, count, kdevname(bh->b_rdev));
}
/**
@@ -1318,6 +1325,11 @@
extern int stram_device_init (void);
#endif
+static void blk_writeback_timer(unsigned long data) +{
+ wakeup_bdflush();
+ wakeup_kupdate();
+}
/**
* end_that_request_first - end I/O on one buffer.
@@ -1373,10 +1385,18 @@
return 0;
}
+extern int laptop_mode;
+
void end_that_request_last(struct request *req) {
struct completion *waiting = req->waiting;
+ /*
+ * schedule the writeout of pending dirty data when the disk is idle
+ */
+ if (laptop_mode && req->cmd == READ)
+ mod_timer(&writeback_timer, jiffies + 5 * HZ);
+
req_finished_io(req);
blkdev_release_request(req);
if (waiting)
@@ -1403,6 +1423,9 @@
blk_max_low_pfn = max_low_pfn - 1;
blk_max_pfn = max_pfn - 1;
+
+ init_timer(&writeback_timer);
+ writeback_timer.function = blk_writeback_timer;
#ifdef CONFIG_AMIGA_Z2RAM
z2_init();
fs/buffer.c +33 -2
@@ -88,6 +88,13 @@static int osync_buffers_list (struct list_head *);static void __refile_buffer (struct buffer_head *); +/* +* A global sysctl-controlled flag which puts the machine into "laptop mode" +*/ +int laptop_mode; + +static DECLARE_WAIT_QUEUE_HEAD (kupdate_wait); +/* This is used by some architectures to estimate available memory. */ atomic_t buffermem_pages =ATOMIC_INIT (0 );
@@ -995,7 +1002,7 @@ dirty *=100 ; dirty_limit = tot * bdf_prm.b_un.nfract_stop_bdflush; -if (dirty > dirty_limit) +if (!laptop_mode && dirty > dirty_limit)return 0 ;return 1 ; }
@@ -1044,6 +1051,8 @@void mark_buffer_dirty (struct buffer_head *bh) {if (!atomic_set_buffer_dirty (bh)) { +if (block_dump) +printk ("%s: dirtied buffer\n" , current->comm);__mark_dirty (bh);balance_dirty (); }
@@ -1055,6 +1064,13 @@ }EXPORT_SYMBOL (set_buffer_flushtime); +unsigned long get_buffer_flushtime (void ) +{ +return bdf_prm.b_un.age_buffer; +} +EXPORT_SYMBOL (get_buffer_flushtime); + +/* * A buffer may need to be moved from one buffer list to another * (e.g. in case it is not shared any more). Handle this.
@@ -2815,6 +2831,12 @@wake_up_interruptible (&bdflush_wait); } +void wakeup_kupdate (void ) +{ +if (waitqueue_active (&kupdate_wait)) +wake_up (&kupdate_wait); +} +/* * Here we attempt to write back old buffers. We also try to flush inodes * and supers as well, since this function is essentially "update", and
@@ -2835,7 +2857,9 @@spin_lock (&lru_list_lock); bh = lru_list[BUF_DIRTY ]; -if (!bh ||time_before (jiffies, bh->b_flushtime)) +if (!bh) +break ; +if (time_before (jiffies, bh->b_flushtime) && !laptop_mode)break ;if (write_some_buffers (NODEV ))continue ;
@@ -2983,6 +3007,10 @@complete ((struct completion *)startup);for (;;) { +DECLARE_WAITQUEUE (wait, tsk); + +add_wait_queue (&kupdate_wait, &wait); +/* update interval */ interval = bdf_prm.b_un.interval;if (interval) {
@@ -2993,6 +3021,7 @@ tsk->state =TASK_STOPPED ;schedule ();/* wait for SIGCONT */ } +remove_wait_queue (&kupdate_wait, &wait);/* check for sigstop */ if (signal_pending (tsk)) {int stopped =0 ;
@@ -3010,6 +3039,8 @@printk (KERN_DEBUG "kupdate() activated...\n" );#endif sync_old_buffers (); +if (laptop_mode) +fsync_dev (NODEV );run_task_queue (&tq_disk); } }
fs/jbd/transaction.c +5 -1
@@ -56,7 +56,11 @@ transaction->t_journal = journal; transaction->t_state =T_RUNNING ; transaction->t_tid = journal->j_transaction_sequence++; - transaction->t_expires = jiffies + journal->j_commit_interval; +/* +* have to do it here, otherwise changed age_buffers since boot +* wont have any effect +*/ + transaction->t_expires = jiffies +get_buffer_flushtime ();INIT_LIST_HEAD (&transaction->t_jcb);/* Set up the commit timer for the new transaction. */
include/linux/fs.h +3 -0
@@ -1238,6 +1238,7 @@ }extern void set_buffer_flushtime (struct buffer_head *); +extern unsigned long get_buffer_flushtime (void );extern void balance_dirty (void );extern int check_disk_change (kdev_t );extern int invalidate_inodes (struct super_block *);
@@ -1427,8 +1428,10 @@return get_hash_table (sb->s_dev, block, sb->s_blocksize); }extern void wakeup_bdflush (void ); +extern void wakeup_kupdate (void );extern void put_unused_buffer_head (struct buffer_head * bh);extern struct buffer_head *get_unused_buffer_head (int async); +extern int block_dump;extern int brw_page (int ,struct page *,kdev_t ,int [],int );
include/linux/sysctl.h +2 -0
@@ -144,6 +144,8 @@VM_MAX_MAP_COUNT =11 ,/* int: Maximum number of active map areas */ VM_MIN_READAHEAD =12 ,/* Min file readahead */ VM_MAX_READAHEAD =13 ,/* Max file readahead */ +VM_LAPTOP_MODE =14 ,/* vm laptop mode */ + VM_BLOCK_DUMP=15, /* dump data read/write and dirtying */ };
kernel/sysctl.c +6 -0
@@ -51,6 +51,8 @@extern int core_uses_pid;extern char core_pattern[];extern int cad_pid; +extern int laptop_mode; +extern int block_dump;/* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ static int maxolduid =65535 ;
@@ -280,6 +282,10 @@ &vm_max_readahead,sizeof(int), 0644, NULL, &proc_dointvec}, {VM_MAX_MAP_COUNT ,"max_map_count" , &max_map_count, sizeof(int), 0644, NULL, &proc_dointvec}, + {VM_LAPTOP_MODE, "laptop_mode", + &laptop_mode, sizeof(int), 0644, NULL, &proc_dointvec}, + {VM_BLOCK_DUMP, "block_dump", + &block_dump, sizeof(int), 0644, NULL, &proc_dointvec}, {0} };
mm/filemap.c +2 -0
@@ -166,6 +166,8 @@if (mapping && mapping->host)mark_inode_dirty_pages (mapping->host); +if (block_dump) +printk ("%s: dirtied page\n" , current->comm); } } }
Hi!
Quoted 10 lines
Made a few tweaks and adjustments:
- If
block_dumpis set, also dump who is marking a page/buffer as dirty. akpm recommended this.- Don't touch default bdflush parameters (see script)
That's about it. I've gotten several mails who really like the patch and that it really adds a non-significant amount of extra battery time. I
Non-significant? Like it adds no time at all?
Quoted 3 lines
consider the patch final at this point.
Patch is against 2.4.21-rc2 (ish)
It looks nice, but without Documentation/laptop_mode, I can't say much more ;-)))).
Pavel
Signature · 2 lines
When do you have a heart between your knees? [Johanka's followup: and *two* hearts?]
Quoted 7 lines · Pavel Machek
Hi!
Quoted 5 lines
Made a few tweaks and adjustments:
- If
block_dumpis set, also dump who is marking a page/buffer as dirty. akpm recommended this.
Quoted 7 lines
Quoted 5 lines
- Don't touch default bdflush parameters (see script)
That's about it. I've gotten several mails who really like the patch and that it really adds a non-significant amount of extra battery time. I
Non-significant? Like it adds no time at all?
Woops, that was reversed :)
Quoted 6 lines
Quoted 3 lines
consider the patch final at this point.
Patch is against 2.4.21-rc2 (ish)
It looks nice, but without
Documentation/laptop_mode, I can't say much more ;-)))).
The attached script basically explained everything, but yes I will add a laptop_mode file as well.
Signature · 1 line
Jens Axboe
Review
Quoted 2 lines · Jens Axboe
(quoted diff) +2
-0+ if (block_dump) + printk("%s: %s block %lu/%u on %s\n", current->comm, rw == WRITE ? "WRITE" : "READ", bh->b_rsector, count, kdevname(bh->b_rdev));
This should be changed to KERN_DEBUG or similar, since if you are logging kernel messages to disk you get stuck in an infinite loop with block_dump:
kjournald: WRITE block 67416/8 on 03:05
kjournald: WRITE block 67424/8 on 03:05
kjournald: WRITE block 67432/8 on 03:05
kjournald: WRITE block 548144/8 on 03:05
kjournald: WRITE block 67440/8 on 03:05
kjournald: WRITE block 67448/8 on 03:05
kjournald: WRITE block 67456/8 on 03:05 :
:
Cheers, Andreas
--
Andreas Dilger
http://sourceforge.net/projects/ext2resize/
http://www-mddsp.enel.ucalgary.ca/People/adilger/
Quoted 7 lines · Andreas Dilger
Review
Quoted 2 lines · Jens Axboe
(quoted diff) +2
-0+ if (block_dump) + printk("%s: %s block %lu/%u on %s\n", current->comm, rw == WRITE ? "WRITE" : "READ", bh->b_rsector, count, kdevname(bh->b_rdev));This should be changed to
KERN_DEBUGor similar, since if you are logging kernel messages to disk you get stuck in an infinite loop withblock_dump:
Quoted 7 lines
kjournald: WRITE block 67416/8 on 03:05
kjournald: WRITE block 67424/8 on 03:05
kjournald: WRITE block 67432/8 on 03:05
kjournald: WRITE block 548144/8 on 03:05
kjournald: WRITE block 67440/8 on 03:05
kjournald: WRITE block 67448/8 on 03:05
kjournald: WRITE block 67456/8 on 03:05
Or just stop syslog, I did mention you had to be careful with it.
Signature · 1 line
Jens Axboe