linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Curt Wohlgemuth <curtw@google.com>
To: Christoph Hellwig <hch@infradead.org>,
	Wu Fengguang <fengguang.wu@intel.com>, Jan Kara <jack@suse.cz>,
	Andrew Morton <akpm@linux-foundation.org>,
	Dave Chinner <david@fromorbit.com>,
Cc: linux-fsdevel@vger.kernel.org, linux-mm@kvack.org,
	Curt Wohlgemuth <curtw@google.com>
Subject: [PATCH 3/3 v3] writeback: Add writeback stats for pages written
Date: Mon, 22 Aug 2011 11:38:47 -0700	[thread overview]
Message-ID: <1314038327-22645-3-git-send-email-curtw@google.com> (raw)
In-Reply-To: <1314038327-22645-1-git-send-email-curtw@google.com>

Add a new file, /proc/writeback, which displays
machine global data for how many pages were cleaned for
which reasons.

These data are also available for each BDI, in
<debugfs mount point>/bdi/<device>/wbstats .

Sample output:

   page: balance_dirty_pages               708
   page: background_writeout           3705522
   page: try_to_free_pages                   0
   page: sync                                0
   page: periodic                       269589
   page: fdatawrite                     831528
   page: laptop_periodic                     0
   page: free_more_memory                    0
   page: fs_free_space                       0

Signed-off-by: Curt Wohlgemuth <curtw@google.com>
---

Changes since v2:

   - Global stats are now in /proc/writeback , not
     /proc/writeback/stats
   - Per-BDI stats are now in
        <debugfs mount point>/bdi/<device>/wbstats
     not in /sys/block/<device>/bdi/writeback_stats
   - Stats now only include pages written for each reason
   - All files are now non-writeable
             

I didn't address two issues raised by Fengguang from v2 of
this patch:

   - Global data could possibly go into /proc/vmstat, instead of
     into the new /proc/writeback file.
   - The form of the stats could be more useful if they specified
     more than just pages for each reason, but also (a) how many
     'work' items were used for each reason; and (b) how many
     chunks of pages were send for each reason.  E.g.:

                              pages  chunks  works  chunk_kb  work_kbps
       balance_dirty_pages     xx       xx     xx      
       background              xx
       sync

Fengguang, I think this might be useful, but it's a fairly
complex change, that I suspect would be better handled in a
separate patch.  What do you think?


 fs/fs-writeback.c           |   10 +++-
 fs/proc/root.c              |    2 +
 include/linux/backing-dev.h |   10 +++
 include/linux/writeback.h   |    2 +
 mm/backing-dev.c            |  143 ++++++++++++++++++++++++++++++++++++++++++-
 mm/filemap.c                |    4 +
 mm/page-writeback.c         |    7 ++-
 7 files changed, 174 insertions(+), 4 deletions(-)

diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index a004fcd..dc5ed10 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -542,6 +542,7 @@ static long writeback_sb_inodes(struct super_block *sb,
 
 	while (!list_empty(&wb->b_io)) {
 		struct inode *inode = wb_inode(wb->b_io.prev);
+		long wrote_this_inode;
 
 		if (inode->i_sb != sb) {
 			if (work->sb) {
@@ -580,8 +581,10 @@ static long writeback_sb_inodes(struct super_block *sb,
 
 		writeback_single_inode(inode, wb, &wbc);
 
-		work->nr_pages -= write_chunk - wbc.nr_to_write;
-		wrote += write_chunk - wbc.nr_to_write;
+		wrote_this_inode = write_chunk - wbc.nr_to_write;
+
+		work->nr_pages -= wrote_this_inode;
+		wrote += wrote_this_inode;
 		if (!(inode->i_state & I_DIRTY))
 			wrote++;
 		if (wbc.pages_skipped) {
@@ -591,6 +594,9 @@ static long writeback_sb_inodes(struct super_block *sb,
 			 */
 			redirty_tail(inode, wb);
 		}
+		bdi_writeback_stat_add(wb->bdi,
+					work->reason,
+					wrote_this_inode);
 		spin_unlock(&inode->i_lock);
 		spin_unlock(&wb->list_lock);
 		iput(inode);
diff --git a/fs/proc/root.c b/fs/proc/root.c
index 9a8a2b7..c0e2412 100644
--- a/fs/proc/root.c
+++ b/fs/proc/root.c
@@ -18,6 +18,7 @@
 #include <linux/bitops.h>
 #include <linux/mount.h>
 #include <linux/pid_namespace.h>
+#include <linux/backing-dev.h>
 
 #include "internal.h"
 
@@ -125,6 +126,7 @@ void __init proc_root_init(void)
 #endif
 	proc_mkdir("bus", NULL);
 	proc_sys_init();
+	proc_writeback_init();
 }
 
 static int proc_root_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index ef85559..8899fec 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -44,6 +44,10 @@ enum bdi_stat_item {
 	NR_BDI_STAT_ITEMS
 };
 
+struct writeback_stats {
+	u64 stats[WB_REASON_MAX];
+};
+
 #define BDI_STAT_BATCH (8*(1+ilog2(nr_cpu_ids)))
 
 struct bdi_writeback {
@@ -72,6 +76,7 @@ struct backing_dev_info {
 	char *name;
 
 	struct percpu_counter bdi_stat[NR_BDI_STAT_ITEMS];
+	struct writeback_stats *wb_stat;
 
 	unsigned long bw_time_stamp;	/* last time write bw is updated */
 	unsigned long written_stamp;	/* pages written at bw_time_stamp */
@@ -96,6 +101,7 @@ struct backing_dev_info {
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debug_dir;
 	struct dentry *debug_stats;
+	struct dentry *debug_wbstats;
 #endif
 };
 
@@ -190,6 +196,10 @@ static inline s64 bdi_stat_sum(struct backing_dev_info *bdi,
 	return sum;
 }
 
+void bdi_writeback_stat_add(struct backing_dev_info *bdi,
+			    enum wb_reason reason, unsigned long value);
+void proc_writeback_init(void);
+
 extern void bdi_writeout_inc(struct backing_dev_info *bdi);
 
 /*
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index bdda069..5168ac9 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -59,6 +59,7 @@ enum wb_reason {
 	WB_REASON_TRY_TO_FREE_PAGES,
 	WB_REASON_SYNC,
 	WB_REASON_PERIODIC,
+	WB_REASON_FDATAWRITE,
 	WB_REASON_LAPTOP_TIMER,
 	WB_REASON_FREE_MORE_MEM,
 	WB_REASON_FS_FREE_SPACE,
@@ -67,6 +68,7 @@ enum wb_reason {
 	WB_REASON_MAX,
 };
 
+
 /*
  * A control structure which tells the writeback code what to do.  These are
  * always on the stack, and hence need no locking.  They are always initialised
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 474bcfe..6613391 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -10,6 +10,8 @@
 #include <linux/module.h>
 #include <linux/writeback.h>
 #include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <trace/events/writeback.h>
 
 static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0);
@@ -42,6 +44,8 @@ LIST_HEAD(bdi_pending_list);
 static struct task_struct *sync_supers_tsk;
 static struct timer_list sync_supers_timer;
 
+static struct writeback_stats *writeback_sys_stats;
+
 static int bdi_sync_supers(void *);
 static void sync_supers_timer_fn(unsigned long);
 
@@ -56,9 +60,77 @@ void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2)
 	}
 }
 
+
+static const char *wb_stats_labels[WB_REASON_MAX] = {
+	[WB_REASON_BALANCE_DIRTY] = "page: balance_dirty_pages",
+	[WB_REASON_BACKGROUND] = "page: background_writeout",
+	[WB_REASON_TRY_TO_FREE_PAGES] = "page: try_to_free_pages",
+	[WB_REASON_SYNC] = "page: sync",
+	[WB_REASON_PERIODIC] = "page: periodic",
+	[WB_REASON_FDATAWRITE] = "page: fdatawrite",
+	[WB_REASON_LAPTOP_TIMER] = "page: laptop_periodic",
+	[WB_REASON_FREE_MORE_MEM] = "page: free_more_memory",
+	[WB_REASON_FS_FREE_SPACE] = "page: fs_free_space",
+};
+
+static void writeback_stats_collect(struct writeback_stats *src,
+			struct writeback_stats *target)
+{
+	int cpu;
+	for_each_online_cpu(cpu) {
+		int stat;
+		struct writeback_stats *stats = per_cpu_ptr(src, cpu);
+		for (stat = 0; stat < WB_REASON_MAX; stat++)
+			target->stats[stat] += stats->stats[stat];
+	}
+}
+
+static size_t writeback_stats_to_str(struct writeback_stats *stats,
+				    char *buf, size_t len)
+{
+	int bufsize = len - 1;
+	int i, printed = 0;
+	for (i = 0; i < WB_REASON_MAX; i++) {
+		const char *label = wb_stats_labels[i];
+		if (label == NULL)
+			continue;
+		printed += snprintf(buf + printed, bufsize - printed,
+				"%-32s %10llu\n", label, stats->stats[i]);
+		if (printed >= bufsize) {
+			buf[len - 1] = '\n';
+			return len;
+		}
+	}
+
+	buf[printed - 1] = '\n';
+	return printed;
+}
+
+static size_t writeback_stats_print(struct writeback_stats *stats,
+				     char *buf, size_t len)
+{
+	struct writeback_stats total;
+	memset(&total, 0, sizeof(total));
+	writeback_stats_collect(stats, &total);
+	return writeback_stats_to_str(&total, buf, len);
+}
+
+static int writeback_seq_show(struct seq_file *m, void *data)
+{
+	char *buf;
+	size_t size;
+	struct writeback_stats *stats = m->private;
+
+	size = seq_get_buf(m, &buf);
+	if (size == 0)
+		return 0;
+	size = writeback_stats_print(stats, buf, size);
+	seq_commit(m, size);
+	return 0;
+}
+
 #ifdef CONFIG_DEBUG_FS
 #include <linux/debugfs.h>
-#include <linux/seq_file.h>
 
 static struct dentry *bdi_debug_root;
 
@@ -132,15 +204,34 @@ static const struct file_operations bdi_debug_stats_fops = {
 	.release	= single_release,
 };
 
+static int bdi_debug_wbstats_open(struct inode *inode, struct file *file)
+{
+	struct backing_dev_info *bdi = inode->i_private;
+	struct writeback_stats *stats = bdi->wb_stat;
+
+	return single_open(file, writeback_seq_show, (void *)stats);
+}
+
+static const struct file_operations bdi_debug_wbstats_fops = {
+	.open		= bdi_debug_wbstats_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
 static void bdi_debug_register(struct backing_dev_info *bdi, const char *name)
 {
 	bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root);
 	bdi->debug_stats = debugfs_create_file("stats", 0444, bdi->debug_dir,
 					       bdi, &bdi_debug_stats_fops);
+	bdi->debug_wbstats = debugfs_create_file("wbstats", 0444,
+						 bdi->debug_dir, bdi,
+						 &bdi_debug_wbstats_fops);
 }
 
 static void bdi_debug_unregister(struct backing_dev_info *bdi)
 {
+	debugfs_remove(bdi->debug_wbstats);
 	debugfs_remove(bdi->debug_stats);
 	debugfs_remove(bdi->debug_dir);
 }
@@ -157,6 +248,7 @@ static inline void bdi_debug_unregister(struct backing_dev_info *bdi)
 }
 #endif
 
+
 static ssize_t read_ahead_kb_store(struct device *dev,
 				  struct device_attribute *attr,
 				  const char *buf, size_t count)
@@ -678,8 +770,13 @@ int bdi_init(struct backing_dev_info *bdi)
 
 	err = prop_local_init_percpu(&bdi->completions);
 
+	bdi->wb_stat = alloc_percpu(struct writeback_stats);
+	if (bdi->wb_stat == NULL)
+		err = -ENOMEM;
+
 	if (err) {
 err:
+		free_percpu(bdi->wb_stat);
 		while (i--)
 			percpu_counter_destroy(&bdi->bdi_stat[i]);
 	}
@@ -712,6 +809,8 @@ void bdi_destroy(struct backing_dev_info *bdi)
 	for (i = 0; i < NR_BDI_STAT_ITEMS; i++)
 		percpu_counter_destroy(&bdi->bdi_stat[i]);
 
+	free_percpu(bdi->wb_stat);
+
 	prop_local_destroy_percpu(&bdi->completions);
 }
 EXPORT_SYMBOL(bdi_destroy);
@@ -854,3 +953,45 @@ out:
 	return ret;
 }
 EXPORT_SYMBOL(wait_iff_congested);
+
+void bdi_writeback_stat_add(struct backing_dev_info *bdi,
+			    enum wb_reason reason, unsigned long value)
+{
+	if (bdi) {
+		struct writeback_stats *stats = bdi->wb_stat;
+
+		BUG_ON(reason >= WB_REASON_MAX);
+		preempt_disable();
+		stats = per_cpu_ptr(stats, smp_processor_id());
+		stats->stats[reason] += value;
+		if (likely(writeback_sys_stats)) {
+			stats = per_cpu_ptr(writeback_sys_stats,
+					    smp_processor_id());
+			stats->stats[reason] += value;
+		}
+		preempt_enable();
+	}
+}
+
+
+static int global_writeback_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, writeback_seq_show,
+					(void *)writeback_sys_stats);
+}
+
+static const struct file_operations global_writeback_ops = {
+	.open           = global_writeback_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+
+void __init proc_writeback_init(void)
+{
+	writeback_sys_stats = alloc_percpu(struct writeback_stats);
+
+	proc_create_data("writeback", S_IRUGO, NULL,
+			&global_writeback_ops, NULL);
+}
diff --git a/mm/filemap.c b/mm/filemap.c
index 645a080..cc93a9c 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -216,6 +216,10 @@ int __filemap_fdatawrite_range(struct address_space *mapping, loff_t start,
 		return 0;
 
 	ret = do_writepages(mapping, &wbc);
+
+	bdi_writeback_stat_add(mapping->backing_dev_info,
+				WB_REASON_FDATAWRITE,
+				LONG_MAX - wbc.nr_to_write);
 	return ret;
 }
 
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 0e78252..36bc09b 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -737,9 +737,14 @@ static void balance_dirty_pages(struct address_space *mapping,
 		 */
 		trace_balance_dirty_start(bdi);
 		if (bdi_nr_reclaimable > task_bdi_thresh) {
-			pages_written += writeback_inodes_wb(&bdi->wb,
+			long wrote;
+			wrote = writeback_inodes_wb(&bdi->wb,
 						write_chunk,
 						WB_REASON_BALANCE_DIRTY);
+			pages_written += wrote;
+			bdi_writeback_stat_add(bdi,
+						WB_REASON_BALANCE_DIRTY,
+						wrote);
 			trace_balance_dirty_written(bdi, pages_written);
 			if (pages_written >= write_chunk)
 				break;		/* We've done our duty */
-- 
1.7.3.1


  parent reply	other threads:[~2011-08-22 18:40 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-08-22 18:38 [PATCH 1/3] writeback: send work item to queue_io, move_expired_inodes Curt Wohlgemuth
2011-08-22 18:38 ` [PATCH 2/3 v3] writeback: Add a 'reason' to wb_writeback_work Curt Wohlgemuth
2011-08-29 16:23   ` Jan Kara
2011-08-29 16:34     ` Jan Kara
2011-08-30 18:06       ` Curt Wohlgemuth
2011-08-22 18:38 ` Curt Wohlgemuth [this message]
2011-08-29 16:36   ` [PATCH 3/3 v3] writeback: Add writeback stats for pages written Jan Kara
2011-08-30 18:13     ` Curt Wohlgemuth
2011-08-30 22:24       ` Jan Kara
2011-08-29 16:21 ` [PATCH 1/3] writeback: send work item to queue_io, move_expired_inodes Jan Kara

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=1314038327-22645-3-git-send-email-curtw@google.com \
    --to=curtw@google.com \
    --cc=akpm@linux-foundation.org \
    --cc=david@fromorbit.com \
    --cc=fengguang.wu@intel.com \
    --cc=hch@infradead.org \
    --cc=jack@suse.cz \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-mm@kvack.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).