[PATCH 2/2] writeback: optimize periodic sync_supers
From: Artem Bityutskiy <hidden>
Date: 2009-07-07 16:15:40
Also in:
lkml
Subsystem:
filesystems (vfs and infrastructure), memory management, memory management - misc, the rest · Maintainers:
Alexander Viro, Christian Brauner, Andrew Morton, David Hildenbrand, Linus Torvalds
From: Artem Bityutskiy <redacted> Subject: [PATCH] writeback: optimize periodic sync_supers The sync_supers thread wakes up every 5 seconds (by default) and writes back all super blocks. It keeps waking up even if there are no dirty super-blocks. This patch improves it and makes sleep if there is nothing to do. This optimization is quite important for small battery-powered devices. Signed-off-by: Artem Bityutskiy <redacted> --- include/linux/fs.h | 5 +---- mm/backing-dev.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index d98d8d1..b968f43 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h@@ -1793,10 +1793,7 @@ int __put_super_and_need_restart(struct super_block *sb); * Note, VFS does not provide any protection for the super block clean/dirty * state. File-systems should take care of this. */ -static inline void mark_sb_dirty(struct super_block *sb) -{ - sb->s_dirt = 1; -} +void mark_sb_dirty(struct super_block *sb); static inline void mark_sb_clean(struct super_block *sb) { sb->s_dirt = 0;
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 75e6c47..4501626 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c@@ -32,6 +32,8 @@ LIST_HEAD(bdi_pending_list); static struct task_struct *sync_supers_tsk; static struct timer_list sync_supers_timer; +static int supers_timer_armed; +static DEFINE_SPINLOCK(supers_timer_lock); static int bdi_sync_supers(void *); static void sync_supers_timer_fn(unsigned long);
@@ -440,6 +442,11 @@ static void bdi_flush_io(struct backing_dev_info *bdi) * or we risk deadlocking on ->s_umount. The longer term solution would be * to implement sync_supers_bdi() or similar and simply do it from the * bdi writeback tasks individually. + * + * Historically this thread woke up periodically, regardless of whether + * there was any dirty super block. However, nowadays it is optimized to + * wake up only when there is something to sync - this is better from the + * power management point of view. */ static int bdi_sync_supers(void *unused) {
@@ -449,10 +456,22 @@ static int bdi_sync_supers(void *unused) set_current_state(TASK_INTERRUPTIBLE); schedule(); + spin_lock(&supers_timer_lock); + supers_timer_armed = -1; + spin_unlock(&supers_timer_lock); + /* * Do this periodically, like kupdated() did before. */ sync_supers(); + + spin_lock(&supers_timer_lock); + if (supers_timer_armed == 1) + /* A super block was marked as dirty meanwhile */ + arm_supers_timer(); + else + supers_timer_armed = 0; + spin_unlock(&supers_timer_lock); } return 0;
@@ -469,9 +488,22 @@ static void arm_supers_timer(void) static void sync_supers_timer_fn(unsigned long unused) { wake_up_process(sync_supers_tsk); - arm_supers_timer(); } +void mark_sb_dirty(struct super_block *sb) +{ + sb->s_dirt = 1; + spin_lock(&supers_timer_lock); + if (!supers_timer_armed) { + arm_supers_timer(); + supers_timer_armed = 1; + } else if (supers_timer_armed == -1) { + supers_timer_armed = 1; + } + spin_unlock(&supers_timer_lock); +} +EXPORT_SYMBOL(mark_sb_dirty); + static int bdi_forker_task(void *ptr) { struct bdi_writeback *me = ptr;
--
1.6.0.6