* [RFC] [PATCH] write-back: introduce a handy helper function
2009-05-22 18:38 [RFC] few hacks to optimize write-back Artem Bityutskiy
@ 2009-05-22 18:38 ` Artem Bityutskiy
2009-05-22 18:38 ` [RFC] [PATCH] write-back: do not wake up unnecessarily Artem Bityutskiy
1 sibling, 0 replies; 3+ messages in thread
From: Artem Bityutskiy @ 2009-05-22 18:38 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Artem Bityutskiy
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Subject: [PATCH] write-back: introduce a handy helper function
This patch is just a clean up and a preparation for the
following wb_timer patches. It introduces a helper
'setup_wb_timer()' function which sets up the periodic
write-back timer to the specified value. The code just
looks neater with this helper.
Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
---
mm/page-writeback.c | 25 +++++++++++++++++++------
1 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index bb553c3..f2496be 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -738,6 +738,21 @@ static DEFINE_TIMER(wb_timer, wb_timer_fn, 0, 0);
static DEFINE_TIMER(laptop_mode_wb_timer, laptop_timer_fn, 0, 0);
/*
+ * Setup the periodic write-back timer to expires at @expires jiffies. If
+ * @expires is zero, the timer is set up to the default interval.
+ */
+static void setup_wb_timer(unsigned long expires)
+{
+ if (!expires) {
+ /* Setup to the default wake-up period value */
+ expires = jiffies;
+ expires += msecs_to_jiffies(dirty_writeback_interval * 10);
+ }
+
+ mod_timer(&wb_timer, expires);
+}
+
+/*
* Periodic writeback of "old" data.
*
* Define "old": the first time one of an inode's pages is dirtied, we mark the
@@ -792,7 +807,7 @@ static void wb_kupdate(unsigned long arg)
if (time_before(next_jif, jiffies + HZ))
next_jif = jiffies + HZ;
if (dirty_writeback_interval)
- mod_timer(&wb_timer, next_jif);
+ setup_wb_timer(next_jif);
}
/*
@@ -803,8 +818,7 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write,
{
proc_dointvec(table, write, file, buffer, length, ppos);
if (dirty_writeback_interval)
- mod_timer(&wb_timer, jiffies +
- msecs_to_jiffies(dirty_writeback_interval * 10));
+ setup_wb_timer(0);
else
del_timer(&wb_timer);
return 0;
@@ -813,7 +827,7 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write,
static void wb_timer_fn(unsigned long unused)
{
if (pdflush_operation(wb_kupdate, 0) < 0)
- mod_timer(&wb_timer, jiffies + HZ); /* delay 1 second */
+ setup_wb_timer( jiffies + HZ); /* delay 1 second */
}
static void laptop_flush(unsigned long unused)
@@ -906,8 +920,7 @@ void __init page_writeback_init(void)
{
int shift;
- mod_timer(&wb_timer,
- jiffies + msecs_to_jiffies(dirty_writeback_interval * 10));
+ setup_wb_timer(0);
writeback_set_ratelimit();
register_cpu_notifier(&ratelimit_nb);
--
1.6.0.6
^ permalink raw reply related [flat|nested] 3+ messages in thread* [RFC] [PATCH] write-back: do not wake up unnecessarily
2009-05-22 18:38 [RFC] few hacks to optimize write-back Artem Bityutskiy
2009-05-22 18:38 ` [RFC] [PATCH] write-back: introduce a handy helper function Artem Bityutskiy
@ 2009-05-22 18:38 ` Artem Bityutskiy
1 sibling, 0 replies; 3+ messages in thread
From: Artem Bityutskiy @ 2009-05-22 18:38 UTC (permalink / raw)
To: linux-fsdevel; +Cc: Artem Bityutskiy
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
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@nokia.com>
---
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
^ permalink raw reply related [flat|nested] 3+ messages in thread