[PATCH] mm/backing-dev: show state of all bdi_writeback in debugfs
From: Konstantin Khlebnikov
Date: Tue Jul 23 2019 - 08:49:38 EST
Currently /sys/kernel/debug/bdi/$maj:$min/stats shows only root bdi wb.
With CONFIG_CGROUP_WRITEBACK=y there is one for each memory cgroup.
This patch shows here state of each bdi_writeback in form:
<global state>
Id: 1
Cgroup: /
<root wb state>
Id: xxx
Cgroup: /path
<cgroup wb state>
Id: yyy
Cgroup: /path2
<cgroup wb state>
...
Signed-off-by: Konstantin Khlebnikov <khlebnikov@xxxxxxxxxxxxxx>
---
mm/backing-dev.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 93 insertions(+), 13 deletions(-)
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index e8e89158adec..3e752c4bafaf 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -45,7 +45,7 @@ static void bdi_debug_init(void)
static int bdi_debug_stats_show(struct seq_file *m, void *v)
{
struct backing_dev_info *bdi = m->private;
- struct bdi_writeback *wb = &bdi->wb;
+ struct bdi_writeback *wb = v;
unsigned long background_thresh;
unsigned long dirty_thresh;
unsigned long wb_thresh;
@@ -65,43 +65,123 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v)
nr_dirty_time++;
spin_unlock(&wb->list_lock);
- global_dirty_limits(&background_thresh, &dirty_thresh);
- wb_thresh = wb_calc_thresh(wb, dirty_thresh);
-
#define K(x) ((x) << (PAGE_SHIFT - 10))
+
+ /* global state */
+ if (wb == &bdi->wb) {
+ global_dirty_limits(&background_thresh, &dirty_thresh);
+ wb_thresh = wb_calc_thresh(wb, dirty_thresh);
+ seq_printf(m,
+ "BdiDirtyThresh: %10lu kB\n"
+ "DirtyThresh: %10lu kB\n"
+ "BackgroundThresh: %10lu kB\n"
+ "bdi_list: %10u\n",
+ K(wb_thresh),
+ K(dirty_thresh),
+ K(background_thresh),
+ !list_empty(&bdi->bdi_list));
+ }
+
+ /* cgroup header */
+#ifdef CONFIG_CGROUP_WRITEBACK
+ if (bdi->capabilities & BDI_CAP_CGROUP_WRITEBACK) {
+ size_t buflen, len;
+ char *buf;
+
+ seq_printf(m, "\nId: %d\nCgroup: ", wb->memcg_css->id);
+ buflen = seq_get_buf(m, &buf);
+ if (buf) {
+ len = cgroup_path(wb->memcg_css->cgroup, buf, buflen);
+ seq_commit(m, len <= buflen ? len : -1);
+ seq_putc(m, '\n');
+ }
+ }
+#endif /* CONFIG_CGROUP_WRITEBACK */
+
seq_printf(m,
"BdiWriteback: %10lu kB\n"
"BdiReclaimable: %10lu kB\n"
- "BdiDirtyThresh: %10lu kB\n"
- "DirtyThresh: %10lu kB\n"
- "BackgroundThresh: %10lu kB\n"
"BdiDirtied: %10lu kB\n"
"BdiWritten: %10lu kB\n"
"BdiWriteBandwidth: %10lu kBps\n"
+ "BdiAvgWriteBwidth: %10lu kBps\n"
"b_dirty: %10lu\n"
"b_io: %10lu\n"
"b_more_io: %10lu\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(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),
+ (unsigned long) K(wb->avg_write_bandwidth),
nr_dirty,
nr_io,
nr_more_io,
nr_dirty_time,
- !list_empty(&bdi->bdi_list), bdi->wb.state);
+ wb->state);
#undef K
return 0;
}
-DEFINE_SHOW_ATTRIBUTE(bdi_debug_stats);
+
+static void *bdi_debug_stats_start(struct seq_file *m, loff_t *ppos)
+{
+ struct backing_dev_info *bdi = m->private;
+ struct bdi_writeback *wb;
+ loff_t pos = *ppos;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node)
+ if (pos-- == 0)
+ return wb;
+ return NULL;
+}
+
+static void *bdi_debug_stats_next(struct seq_file *m, void *v, loff_t *ppos)
+{
+ struct backing_dev_info *bdi = m->private;
+ struct bdi_writeback *wb = v;
+
+ list_for_each_entry_continue_rcu(wb, &bdi->wb_list, bdi_node) {
+ ++*ppos;
+ return wb;
+ }
+ return NULL;
+}
+
+static void bdi_debug_stats_stop(struct seq_file *m, void *v)
+{
+ rcu_read_unlock();
+}
+
+static const struct seq_operations bdi_debug_stats_seq_ops = {
+ .start = bdi_debug_stats_start,
+ .next = bdi_debug_stats_next,
+ .stop = bdi_debug_stats_stop,
+ .show = bdi_debug_stats_show,
+};
+
+static int bdi_debug_stats_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *m;
+ int ret;
+
+ ret = seq_open(file, &bdi_debug_stats_seq_ops);
+ if (!ret) {
+ m = file->private_data;
+ m->private = inode->i_private;
+ }
+ return ret;
+}
+
+static const struct file_operations bdi_debug_stats_fops = {
+ .open = bdi_debug_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
static void bdi_debug_register(struct backing_dev_info *bdi, const char *name)
{