lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20090527130706.4913.42520.sendpatchset@localhost.localdomain>
Date:	Wed, 27 May 2009 16:07:06 +0300
From:	Artem Bityutskiy <Artem.Bityutskiy@...ia.com>
To:	linux-kernel@...r.kernel.org
Cc:	linux-fsdevel@...r.kernel.org,
	Christoph Hellwig <hch@...radead.org>,
	Artem Bityutskiy <Artem.Bityutskiy@...ia.com>
Subject: [PATCH 19/19] periodic write-back: do not wake up unnecessarily

From: Artem Bityutskiy <Artem.Bityutskiy@...ia.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@...ia.com>
Cc: Christoph Hellwig <hch@...radead.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

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ