#include <linux/debugfs.h>
 #include <linux/seq_file.h>
 
+struct wb_stats {
+       unsigned long nr_dirty;
+       unsigned long nr_io;
+       unsigned long nr_more_io;
+       unsigned long nr_dirty_time;
+       unsigned long nr_writeback;
+       unsigned long nr_reclaimable;
+       unsigned long nr_dirtied;
+       unsigned long nr_written;
+       unsigned long dirty_thresh;
+       unsigned long wb_thresh;
+};
+
 static struct dentry *bdi_debug_root;
 
 static void bdi_debug_init(void)
        bdi_debug_root = debugfs_create_dir("bdi", NULL);
 }
 
-static int bdi_debug_stats_show(struct seq_file *m, void *v)
+static void collect_wb_stats(struct wb_stats *stats,
+                            struct bdi_writeback *wb)
 {
-       struct backing_dev_info *bdi = m->private;
-       struct bdi_writeback *wb = &bdi->wb;
-       unsigned long background_thresh;
-       unsigned long dirty_thresh;
-       unsigned long wb_thresh;
-       unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time;
        struct inode *inode;
 
-       nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0;
        spin_lock(&wb->list_lock);
        list_for_each_entry(inode, &wb->b_dirty, i_io_list)
-               nr_dirty++;
+               stats->nr_dirty++;
        list_for_each_entry(inode, &wb->b_io, i_io_list)
-               nr_io++;
+               stats->nr_io++;
        list_for_each_entry(inode, &wb->b_more_io, i_io_list)
-               nr_more_io++;
+               stats->nr_more_io++;
        list_for_each_entry(inode, &wb->b_dirty_time, i_io_list)
                if (inode->i_state & I_DIRTY_TIME)
-                       nr_dirty_time++;
+                       stats->nr_dirty_time++;
        spin_unlock(&wb->list_lock);
 
+       stats->nr_writeback += wb_stat(wb, WB_WRITEBACK);
+       stats->nr_reclaimable += wb_stat(wb, WB_RECLAIMABLE);
+       stats->nr_dirtied += wb_stat(wb, WB_DIRTIED);
+       stats->nr_written += wb_stat(wb, WB_WRITTEN);
+       stats->wb_thresh += wb_calc_thresh(wb, stats->dirty_thresh);
+}
+
+#ifdef CONFIG_CGROUP_WRITEBACK
+static void bdi_collect_stats(struct backing_dev_info *bdi,
+                             struct wb_stats *stats)
+{
+       struct bdi_writeback *wb;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) {
+               if (!wb_tryget(wb))
+                       continue;
+
+               collect_wb_stats(stats, wb);
+               wb_put(wb);
+       }
+       rcu_read_unlock();
+}
+#else
+static void bdi_collect_stats(struct backing_dev_info *bdi,
+                             struct wb_stats *stats)
+{
+       collect_wb_stats(stats, &bdi->wb);
+}
+#endif
+
+static int bdi_debug_stats_show(struct seq_file *m, void *v)
+{
+       struct backing_dev_info *bdi = m->private;
+       unsigned long background_thresh;
+       unsigned long dirty_thresh;
+       struct wb_stats stats;
+       unsigned long tot_bw;
+
        global_dirty_limits(&background_thresh, &dirty_thresh);
-       wb_thresh = wb_calc_thresh(wb, dirty_thresh);
+
+       memset(&stats, 0, sizeof(stats));
+       stats.dirty_thresh = dirty_thresh;
+       bdi_collect_stats(bdi, &stats);
+       tot_bw = atomic_long_read(&bdi->tot_write_bandwidth);
 
        seq_printf(m,
                   "BdiWriteback:       %10lu kB\n"
                   "b_dirty_time:       %10lu\n"
                   "bdi_list:           %10u\n"
                   "state:              %10lx\n",
-                  (unsigned long) K(wb_stat(wb, WB_WRITEBACK)),
-                  (unsigned long) K(wb_stat(wb, WB_RECLAIMABLE)),
-                  K(wb_thresh),
+                  K(stats.nr_writeback),
+                  K(stats.nr_reclaimable),
+                  K(stats.wb_thresh),
                   K(dirty_thresh),
                   K(background_thresh),
-                  (unsigned long) K(wb_stat(wb, WB_DIRTIED)),
-                  (unsigned long) K(wb_stat(wb, WB_WRITTEN)),
-                  (unsigned long) K(wb->write_bandwidth),
-                  nr_dirty,
-                  nr_io,
-                  nr_more_io,
-                  nr_dirty_time,
+                  K(stats.nr_dirtied),
+                  K(stats.nr_written),
+                  K(tot_bw),
+                  stats.nr_dirty,
+                  stats.nr_io,
+                  stats.nr_more_io,
+                  stats.nr_dirty_time,
                   !list_empty(&bdi->bdi_list), bdi->wb.state);
 
        return 0;