linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
To: linux-fsdevel@vger.kernel.org
Cc: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Subject: [RFC] [PATCH] write-back: do not wake up unnecessarily
Date: Fri, 22 May 2009 21:38:33 +0300	[thread overview]
Message-ID: <20090522183833.29556.38971.sendpatchset@localhost.localdomain> (raw)
In-Reply-To: <20090522183820.29556.68026.sendpatchset@localhost.localdomain>

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


      parent reply	other threads:[~2009-05-22 16:44 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090522183833.29556.38971.sendpatchset@localhost.localdomain \
    --to=artem.bityutskiy@nokia.com \
    --cc=linux-fsdevel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).