From: Artem Bityutskiy <Artem.Bityutskiy@xxxxxxxxx> Subject: [PATCH] write-back: do not wake up unnecessarily This patch is an optimization, targeted to lessen idle mode power consumption. At the moment the periodic write-back threads (pdflush) are woken up every 5 seconds (by default). They wake up and flush dirty data which is old enough. And even if there are no dirty date, they still wake up. This patch makes the periodic write-back wake up only when there are dirty data. Otherwise they just sleep and do not interfere CPU which is sleeping/being in a low frequency and low power consumption mode. Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@xxxxxxxxx> --- fs/fs-writeback.c | 17 ++++++++++ include/linux/writeback.h | 3 ++ mm/page-writeback.c | 74 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 91013ff..0b233f7 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -94,6 +94,7 @@ static void writeback_release(struct backing_dev_info *bdi) void __mark_inode_dirty(struct inode *inode, int flags) { struct super_block *sb = inode->i_sb; + int start_pwb = 0; /* * Don't do this for I_DIRTY_PAGES - that doesn't actually @@ -164,10 +165,26 @@ void __mark_inode_dirty(struct inode *inode, int flags) if (!was_dirty) { inode->dirtied_when = jiffies; list_move(&inode->i_list, &sb->s_dirty); + /* + * If periodic write-back is disabled - enable it + * again. + */ + if (periodic_wb_stopped) { + struct backing_dev_info *bdi; + + bdi = inode->i_mapping->backing_dev_info; + if (bdi_cap_writeback_dirty(bdi)) { + start_pwb = 1; + periodic_wb_stopped = 0; + } + } } } out: spin_unlock(&inode_lock); + + if (start_pwb) + start_periodic_wb(); } EXPORT_SYMBOL(__mark_inode_dirty); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 9344547..f892089 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -13,6 +13,9 @@ extern spinlock_t inode_lock; extern struct list_head inode_in_use; extern struct list_head inode_unused; +extern int periodic_wb_stopped; +extern void start_periodic_wb(void); + /* * Yes, writeback.h requires sched.h * No, sched.h is not included from here. diff --git a/mm/page-writeback.c b/mm/page-writeback.c index f2496be..a9a915b 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -738,6 +738,13 @@ static DEFINE_TIMER(wb_timer, wb_timer_fn, 0, 0); static DEFINE_TIMER(laptop_mode_wb_timer, laptop_timer_fn, 0, 0); /* + * We stop periodic write-back if there are no dirty inodes and superblocks in + * the system. This global flag indicates whether the periodic write-back is + * enabled or disabled. It is protected by the 'inode_lock'. + */ +int periodic_wb_stopped; + +/* * Setup the periodic write-back timer to expires at @expires jiffies. If * @expires is zero, the timer is set up to the default interval. */ @@ -753,6 +760,59 @@ static void setup_wb_timer(unsigned long expires) } /* + * Start the periodic write-back. This function is usually called when + * something becomes dirty and the periodic write-back should be enabled. + */ +void start_periodic_wb(void) +{ + if (dirty_writeback_interval) + setup_wb_timer(0); +} + +/* + * Check whether the periodic update is needed. This function walks all + * superblocks and checks if everything is clean or not. If not, then the + * periodic update should stay enabled, otherwise it is OK to disable it. + */ +static int periodic_wb_needed(void) +{ + int ret = 1; + struct super_block *sb; + + spin_lock(&sb_lock); + spin_lock(&inode_lock); + if (periodic_wb_stopped) { + ret = 0; + goto out; + } + + list_for_each_entry(sb, &super_blocks, s_list) { + if (sb->s_dirt) + goto out; + + if (sb_has_dirty_inodes(sb)) { + struct inode *inode; + struct backing_dev_info *bdi; + + inode = list_entry(sb->s_inodes.next, + struct inode, i_sb_list); + bdi = inode->i_mapping->backing_dev_info; + if (!bdi_cap_writeback_dirty(bdi)) + continue; + goto out; + } + } + + periodic_wb_stopped = 1; + ret = 0; + +out: + spin_unlock(&inode_lock); + spin_unlock(&sb_lock); + return ret; +} + +/* * Periodic writeback of "old" data. * * Define "old": the first time one of an inode's pages is dirtied, we mark the @@ -804,10 +864,16 @@ static void wb_kupdate(unsigned long arg) } nr_to_write -= MAX_WRITEBACK_PAGES - wbc.nr_to_write; } - if (time_before(next_jif, jiffies + HZ)) - next_jif = jiffies + HZ; - if (dirty_writeback_interval) - setup_wb_timer(next_jif); + + if (dirty_writeback_interval) { + /* Schedule write-back only if there is dirty data */ + if (periodic_wb_needed()) { + if (time_before(next_jif, jiffies + HZ)) + next_jif = jiffies + HZ; + setup_wb_timer(next_jif); + } else + del_timer(&wb_timer); + } } /* -- 1.6.0.6 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html