[RFC] [PATCH] write-back: do not wake up unnecessarily

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux