From: Kent Overstreet <[email protected]>

Signed-off-by: Kent Overstreet <[email protected]>
Reviewed-by: Darrick J. Wong <[email protected]>
Signed-off-by: Darrick J. Wong <[email protected]>
---
 fs/bcachefs/super.c |    2 +
 fs/bcachefs/util.c  |  133 +++++++++++++++++++++++++++++++++++++++++++++------
 fs/bcachefs/util.h  |    4 ++
 3 files changed, 123 insertions(+), 16 deletions(-)


diff --git a/fs/bcachefs/super.c b/fs/bcachefs/super.c
index 6b23e11825e6d..b9e2c1032b920 100644
--- a/fs/bcachefs/super.c
+++ b/fs/bcachefs/super.c
@@ -1262,6 +1262,8 @@ static struct bch_dev *__bch2_dev_alloc(struct bch_fs *c,
 
        bch2_time_stats_init(&ca->io_latency[READ]);
        bch2_time_stats_init(&ca->io_latency[WRITE]);
+       ca->io_latency[READ].quantiles_enabled = true;
+       ca->io_latency[WRITE].quantiles_enabled = true;
 
        ca->mi = bch2_mi_to_cpu(member);
 
diff --git a/fs/bcachefs/util.c b/fs/bcachefs/util.c
index 902f6b1a8a142..4c63f81e18bc4 100644
--- a/fs/bcachefs/util.c
+++ b/fs/bcachefs/util.c
@@ -506,10 +506,8 @@ static inline void pr_name_and_units(struct printbuf *out, 
const char *name, u64
 
 void bch2_time_stats_to_text(struct printbuf *out, struct bch2_time_stats 
*stats)
 {
-       const struct time_unit *u;
        s64 f_mean = 0, d_mean = 0;
-       u64 q, last_q = 0, f_stddev = 0, d_stddev = 0;
-       int i;
+       u64 f_stddev = 0, d_stddev = 0;
 
        if (stats->buffer) {
                int cpu;
@@ -608,19 +606,122 @@ void bch2_time_stats_to_text(struct printbuf *out, 
struct bch2_time_stats *stats
 
        printbuf_tabstops_reset(out);
 
-       i = eytzinger0_first(NR_QUANTILES);
-       u = pick_time_units(stats->quantiles.entries[i].m);
-
-       prt_printf(out, "quantiles (%s):\t", u->name);
-       eytzinger0_for_each(i, NR_QUANTILES) {
-               bool is_last = eytzinger0_next(i, NR_QUANTILES) == -1;
-
-               q = max(stats->quantiles.entries[i].m, last_q);
-               prt_printf(out, "%llu ",
-                      div_u64(q, u->nsecs));
-               if (is_last)
-                       prt_newline(out);
-               last_q = q;
+       if (stats->quantiles_enabled) {
+               int i = eytzinger0_first(NR_QUANTILES);
+               const struct time_unit *u =
+                       pick_time_units(stats->quantiles.entries[i].m);
+               u64 last_q = 0;
+
+               prt_printf(out, "quantiles (%s):\t", u->name);
+               eytzinger0_for_each(i, NR_QUANTILES) {
+                       bool is_last = eytzinger0_next(i, NR_QUANTILES) == -1;
+
+                       u64 q = max(stats->quantiles.entries[i].m, last_q);
+                       prt_printf(out, "%llu ", div_u64(q, u->nsecs));
+                       if (is_last)
+                               prt_newline(out);
+                       last_q = q;
+               }
+       }
+}
+
+#include <linux/seq_buf.h>
+
+static void seq_buf_time_units_aligned(struct seq_buf *out, u64 ns)
+{
+       const struct time_unit *u = pick_time_units(ns);
+
+       seq_buf_printf(out, "%8llu %s", div64_u64(ns, u->nsecs), u->name);
+}
+
+void bch2_time_stats_to_seq_buf(struct seq_buf *out, struct bch2_time_stats 
*stats)
+{
+       s64 f_mean = 0, d_mean = 0;
+       u64 f_stddev = 0, d_stddev = 0;
+
+       if (stats->buffer) {
+               int cpu;
+
+               spin_lock_irq(&stats->lock);
+               for_each_possible_cpu(cpu)
+                       __bch2_time_stats_clear_buffer(stats, 
per_cpu_ptr(stats->buffer, cpu));
+               spin_unlock_irq(&stats->lock);
+       }
+
+       /*
+        * avoid divide by zero
+        */
+       if (stats->freq_stats.n) {
+               f_mean = mean_and_variance_get_mean(stats->freq_stats);
+               f_stddev = mean_and_variance_get_stddev(stats->freq_stats);
+               d_mean = mean_and_variance_get_mean(stats->duration_stats);
+               d_stddev = mean_and_variance_get_stddev(stats->duration_stats);
+       }
+
+       seq_buf_printf(out, "count: %llu\n", stats->duration_stats.n);
+
+       seq_buf_printf(out, "                       since mount        
recent\n");
+
+       seq_buf_printf(out, "duration of events\n");
+
+       seq_buf_printf(out, "  min:                     ");
+       seq_buf_time_units_aligned(out, stats->min_duration);
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "  max:                     ");
+       seq_buf_time_units_aligned(out, stats->max_duration);
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "  total:                   ");
+       seq_buf_time_units_aligned(out, stats->total_duration);
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "  mean:                    ");
+       seq_buf_time_units_aligned(out, d_mean);
+       seq_buf_time_units_aligned(out, 
mean_and_variance_weighted_get_mean(stats->duration_stats_weighted));
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "  stddev:                  ");
+       seq_buf_time_units_aligned(out, d_stddev);
+       seq_buf_time_units_aligned(out, 
mean_and_variance_weighted_get_stddev(stats->duration_stats_weighted));
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "time between events\n");
+
+       seq_buf_printf(out, "  min:                     ");
+       seq_buf_time_units_aligned(out, stats->min_freq);
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "  max:                     ");
+       seq_buf_time_units_aligned(out, stats->max_freq);
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "  mean:                    ");
+       seq_buf_time_units_aligned(out, f_mean);
+       seq_buf_time_units_aligned(out, 
mean_and_variance_weighted_get_mean(stats->freq_stats_weighted));
+       seq_buf_printf(out, "\n");
+
+       seq_buf_printf(out, "  stddev:                  ");
+       seq_buf_time_units_aligned(out, f_stddev);
+       seq_buf_time_units_aligned(out, 
mean_and_variance_weighted_get_stddev(stats->freq_stats_weighted));
+       seq_buf_printf(out, "\n");
+
+       if (stats->quantiles_enabled) {
+               int i = eytzinger0_first(NR_QUANTILES);
+               const struct time_unit *u =
+                       pick_time_units(stats->quantiles.entries[i].m);
+               u64 last_q = 0;
+
+               prt_printf(out, "quantiles (%s):\t", u->name);
+               eytzinger0_for_each(i, NR_QUANTILES) {
+                       bool is_last = eytzinger0_next(i, NR_QUANTILES) == -1;
+
+                       u64 q = max(stats->quantiles.entries[i].m, last_q);
+                       seq_buf_printf(out, "%llu ", div_u64(q, u->nsecs));
+                       if (is_last)
+                               seq_buf_printf(out, "\n");
+                       last_q = q;
+               }
        }
 }
 #else
diff --git a/fs/bcachefs/util.h b/fs/bcachefs/util.h
index c3b11c3d24ea9..7ff2d4fe26f68 100644
--- a/fs/bcachefs/util.h
+++ b/fs/bcachefs/util.h
@@ -382,6 +382,7 @@ struct bch2_time_stat_buffer {
 
 struct bch2_time_stats {
        spinlock_t      lock;
+       bool            quantiles_enabled;
        /* all fields are in nanoseconds */
        u64             min_duration;
        u64             max_duration;
@@ -435,6 +436,9 @@ static inline bool track_event_change(struct 
bch2_time_stats *stats,
 
 void bch2_time_stats_to_text(struct printbuf *, struct bch2_time_stats *);
 
+struct seq_buf;
+void bch2_time_stats_to_seq_buf(struct seq_buf *, struct bch2_time_stats *);
+
 void bch2_time_stats_exit(struct bch2_time_stats *);
 void bch2_time_stats_init(struct bch2_time_stats *);
 


Reply via email to