public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
To: linux-kernel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org,
	Christoph Hellwig <hch@infradead.org>,
	Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Subject: [PATCH 19/19] periodic write-back: do not wake up unnecessarily
Date: Wed, 27 May 2009 16:07:06 +0300	[thread overview]
Message-ID: <20090527130706.4913.42520.sendpatchset@localhost.localdomain> (raw)
In-Reply-To: <20090527130503.4913.62042.sendpatchset@localhost.localdomain>

From: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Subject: [PATCH] periodic write-back: do not wake up unnecessarily

This patch is an optimization which is targeted to lessen
power consumption.

At the moment the periodic write-back thread (pdflush) is
woken up every 5 seconds (by default). It wake up and
writes back old dirty dirty data. And even if there are
no dirty data, the thread keeps waking up to find no
dirty data. And it repeats this every 5 seconds.

This patch makes the periodic write-back thread wake up
only when there are dirty data. Otherwise it just sleeps
and does not disturb CPU. Indeed, CPU may be resting in
a low frequency and low power consumption mode - why do
we have to distract it unnecessarily?

Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
Cc: Christoph Hellwig <hch@infradead.org>
---
 fs/fs-writeback.c   |   41 ++++++++++++++++++++++++++++
 include/linux/fs.h  |    7 ++---
 mm/page-writeback.c |   74 ++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 114 insertions(+), 8 deletions(-)

diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index e0fb2e7..133944c 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -65,6 +65,24 @@ static void writeback_release(struct backing_dev_info *bdi)
 }
 
 /**
+ * enable_pwb - enable periodic write-back after an inode was marked as dirty.
+ * @inode: the inode which was marked as dirty
+ *
+ * This is a helper function for '__mark_inode_dirty()' which enables the
+ * periodic write-back, unless:
+ *   * the backing device @inode belongs to does not support write-back;
+ *   * periodic write-back is already enabled.
+ */
+static void enable_pwb(struct inode *inode)
+{
+	struct backing_dev_info *bdi = inode->i_mapping->backing_dev_info;
+
+	if (bdi_cap_writeback_dirty(bdi) &&
+	    atomic_add_unless(&periodic_wb_enabled, 1, 1))
+		enable_periodic_wb();
+}
+
+/**
  *	__mark_inode_dirty -	internal function
  *	@inode: inode to mark
  *	@flags: what kind of dirty (i.e. I_DIRTY_SYNC)
@@ -164,6 +182,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 		if (!was_dirty) {
 			inode->dirtied_when = jiffies;
 			list_move(&inode->i_list, &sb->s_dirty);
+			enable_pwb(inode);
 		}
 	}
 out:
@@ -172,6 +191,28 @@ out:
 
 EXPORT_SYMBOL(__mark_inode_dirty);
 
+/**
+ * mark_sb_dirty - mark super block as dirty.
+ * @sb: the super block to mark as dirty
+ *
+ * This function marks super block @sb as dirty and enables the periodic
+ * write-back, unless it is already enabled. Note, VFS does not serialize the
+ * super block clean/dirty (@sb->s_dirt) state changes, and each FS is
+ * responsible for doing its own serialization.
+ */
+void mark_sb_dirty(struct super_block *sb)
+{
+	sb->s_dirt = 1;
+	/*
+	 * If 'periodic_wb_enabled' is 0, set it to 1 and enable the periodic
+	 * write-back.
+	 */
+	if (atomic_add_unless(&periodic_wb_enabled, 1, 1))
+		enable_periodic_wb();
+}
+
+EXPORT_SYMBOL(mark_sb_dirty);
+
 static int write_inode(struct inode *inode, int sync)
 {
 	if (inode->i_sb->s_op->write_inode && !is_bad_inode(inode))
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 437939c..230e308 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1782,10 +1782,9 @@ extern int get_sb_pseudo(struct file_system_type *, char *,
 extern void simple_set_mnt(struct vfsmount *mnt, struct super_block *sb);
 int __put_super_and_need_restart(struct super_block *sb);
 
-static inline void mark_sb_dirty(struct super_block *sb)
-{
-	sb->s_dirt = 1;
-}
+extern atomic_t periodic_wb_enabled;
+extern void enable_periodic_wb(void);
+extern 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/page-writeback.c b/mm/page-writeback.c
index 8a213a9..1cbb858 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -736,6 +736,9 @@ static void laptop_timer_fn(unsigned long unused);
 
 static DEFINE_TIMER(wb_timer, wb_timer_fn, 0, 0);
 static DEFINE_TIMER(laptop_mode_wb_timer, laptop_timer_fn, 0, 0);
+static DEFINE_SPINLOCK(wb_timer_lock);
+
+atomic_t periodic_wb_enabled;
 
 /*
  * Setup the periodic write-back timer to expires at @expires jiffies. If
@@ -753,6 +756,67 @@ static void setup_wb_timer(unsigned long expires)
 }
 
 /*
+ * Enable the periodic write-back. This function is usually called when
+ * an inode or a super block becomes dirty.
+ */
+void enable_periodic_wb(void)
+{
+	if (dirty_writeback_interval) {
+		spin_lock(&wb_timer_lock);
+		setup_wb_timer(0);
+		spin_unlock(&wb_timer_lock);
+	}
+}
+
+static void set_next_wb_timer(unsigned long expires)
+{
+	int all_clean = 1;
+	struct super_block *sb;
+
+	atomic_set(&periodic_wb_enabled, 0);
+
+	spin_lock(&sb_lock);
+	list_for_each_entry(sb, &super_blocks, s_list) {
+		if (sb->s_dirt)
+			break;
+
+		spin_lock(&inode_lock);
+		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)) {
+				spin_unlock(&inode_lock);
+				all_clean = 0;
+				break;
+			}
+		}
+		spin_unlock(&inode_lock);
+	}
+	spin_unlock(&sb_lock);
+
+	spin_lock(&wb_timer_lock);
+	if (all_clean && atomic_read(&periodic_wb_enabled)) {
+		/*
+		 * There are no dirty data, and no one marked an inode or
+		 * super block as dirty. The periodic update timer may be
+		 * deleted. Note, if we race with some other task which has
+		 * just marked something as dirty and just set
+		 * 'periodic_wb_enabled' to 1, then this task will call
+		 * 'enable_periodic_wb()' which will re-enable the 'wb_timer'.
+		 */
+		del_timer(&wb_timer);
+	} else {
+		atomic_set(&periodic_wb_enabled, 1);
+		setup_wb_timer(expires);
+	}
+	spin_unlock(&wb_timer_lock);
+}
+
+/*
  * Periodic writeback of "old" data.
  *
  * Define "old": the first time one of an inode's pages is dirtied, we mark the
@@ -804,10 +868,12 @@ 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) {
+		if (time_before(next_jif, jiffies + HZ))
+			next_jif = jiffies + HZ;
+		set_next_wb_timer(next_jif);
+	}
 }
 
 /*
-- 
1.6.0.6


      parent reply	other threads:[~2009-05-27 11:18 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-05-27 13:05 [PATCH 00/19] periodic write-back timer optimization Artem Bityutskiy
2009-05-27 12:23 ` Arnd Bergmann
2009-05-27 12:27   ` Artem Bityutskiy
2009-05-27 12:33     ` Jens Axboe
2009-05-27 12:37       ` Artem Bityutskiy
2009-05-27 12:47         ` Jens Axboe
2009-05-28  6:34           ` Artem Bityutskiy
2009-05-28  8:18             ` Jens Axboe
2009-06-03 11:13               ` Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 01/19] VFS: introduce helpers for manipulation s_dirty flag Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 02/19] AFFS: do not manipulate s_dirt directly Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 03/19] BFS: " Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 04/19] EXOFS: " Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 05/19] EXT2: " Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 06/19] EXT4: " Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 07/19] FAT: " Artem Bityutskiy
2009-05-27 13:05 ` [PATCH 08/19] HFS: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 09/19] HFSPLUS: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 10/19] JFFS2: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 11/19] NILFS: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 12/19] reiserfs: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 13/19] SYSV: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 14/19] UDF: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 15/19] UFS: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 16/19] XFS: " Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 17/19] VFS: use sb_is_dirty helper Artem Bityutskiy
2009-05-27 13:06 ` [PATCH 18/19] write-back: introduce a helper function Artem Bityutskiy
2009-05-27 13:07 ` 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=20090527130706.4913.42520.sendpatchset@localhost.localdomain \
    --to=artem.bityutskiy@nokia.com \
    --cc=hch@infradead.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@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