1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3 * bch2_time_stats - collect statistics on events that have a duration, with nicely
4 * formatted textual output on demand
5 *
6 * - percpu buffering of event collection: cheap enough to shotgun
7 * everywhere without worrying about overhead
8 *
9 * tracks:
10 * - number of events
11 * - maximum event duration ever seen
12 * - sum of all event durations
13 * - average event duration, standard and weighted
14 * - standard deviation of event durations, standard and weighted
15 * and analagous statistics for the frequency of events
16 *
17 * We provide both mean and weighted mean (exponentially weighted), and standard
18 * deviation and weighted standard deviation, to give an efficient-to-compute
19 * view of current behaviour versus. average behaviour - "did this event source
20 * just become wonky, or is this typical?".
21 *
22 * Particularly useful for tracking down latency issues.
23 */
24 #ifndef _BCACHEFS_TIME_STATS_H
25 #define _BCACHEFS_TIME_STATS_H
26
27 #include <linux/sched/clock.h>
28 #include <linux/spinlock_types.h>
29 #include <linux/string.h>
30
31 #include "mean_and_variance.h"
32
33 struct time_unit {
34 const char *name;
35 u64 nsecs;
36 };
37
38 /*
39 * given a nanosecond value, pick the preferred time units for printing:
40 */
41 const struct time_unit *bch2_pick_time_units(u64 ns);
42
43 /*
44 * quantiles - do not use:
45 *
46 * Only enabled if bch2_time_stats->quantiles_enabled has been manually set - don't
47 * use in new code.
48 */
49
50 #define NR_QUANTILES 15
51 #define QUANTILE_IDX(i) inorder_to_eytzinger0(i, NR_QUANTILES)
52 #define QUANTILE_FIRST eytzinger0_first(NR_QUANTILES)
53 #define QUANTILE_LAST eytzinger0_last(NR_QUANTILES)
54
55 struct quantiles {
56 struct quantile_entry {
57 u64 m;
58 u64 step;
59 } entries[NR_QUANTILES];
60 };
61
62 struct time_stat_buffer {
63 unsigned nr;
64 struct time_stat_buffer_entry {
65 u64 start;
66 u64 end;
67 } entries[31];
68 };
69
70 struct bch2_time_stats {
71 spinlock_t lock;
72 bool have_quantiles;
73 struct time_stat_buffer __percpu *buffer;
74 /* all fields are in nanoseconds */
75 u64 min_duration;
76 u64 max_duration;
77 u64 total_duration;
78 u64 max_freq;
79 u64 min_freq;
80 u64 last_event;
81 u64 last_event_start;
82
83 struct mean_and_variance duration_stats;
84 struct mean_and_variance freq_stats;
85
86 /* default weight for weighted mean and variance calculations */
87 #define TIME_STATS_MV_WEIGHT 8
88
89 struct mean_and_variance_weighted duration_stats_weighted;
90 struct mean_and_variance_weighted freq_stats_weighted;
91 };
92
93 struct bch2_time_stats_quantiles {
94 struct bch2_time_stats stats;
95 struct quantiles quantiles;
96 };
97
time_stats_to_quantiles(struct bch2_time_stats * stats)98 static inline struct quantiles *time_stats_to_quantiles(struct bch2_time_stats *stats)
99 {
100 return stats->have_quantiles
101 ? &container_of(stats, struct bch2_time_stats_quantiles, stats)->quantiles
102 : NULL;
103 }
104
105 void __bch2_time_stats_clear_buffer(struct bch2_time_stats *, struct time_stat_buffer *);
106 void __bch2_time_stats_update(struct bch2_time_stats *stats, u64, u64);
107
108 /**
109 * time_stats_update - collect a new event being tracked
110 *
111 * @stats - bch2_time_stats to update
112 * @start - start time of event, recorded with local_clock()
113 *
114 * The end duration of the event will be the current time
115 */
bch2_time_stats_update(struct bch2_time_stats * stats,u64 start)116 static inline void bch2_time_stats_update(struct bch2_time_stats *stats, u64 start)
117 {
118 __bch2_time_stats_update(stats, start, local_clock());
119 }
120
121 /**
122 * track_event_change - track state change events
123 *
124 * @stats - bch2_time_stats to update
125 * @v - new state, true or false
126 *
127 * Use this when tracking time stats for state changes, i.e. resource X becoming
128 * blocked/unblocked.
129 */
track_event_change(struct bch2_time_stats * stats,bool v)130 static inline bool track_event_change(struct bch2_time_stats *stats, bool v)
131 {
132 if (v != !!stats->last_event_start) {
133 if (!v) {
134 bch2_time_stats_update(stats, stats->last_event_start);
135 stats->last_event_start = 0;
136 } else {
137 stats->last_event_start = local_clock() ?: 1;
138 return true;
139 }
140 }
141
142 return false;
143 }
144
145 void bch2_time_stats_reset(struct bch2_time_stats *);
146 void bch2_time_stats_exit(struct bch2_time_stats *);
147 void bch2_time_stats_init(struct bch2_time_stats *);
148
bch2_time_stats_quantiles_exit(struct bch2_time_stats_quantiles * statq)149 static inline void bch2_time_stats_quantiles_exit(struct bch2_time_stats_quantiles *statq)
150 {
151 bch2_time_stats_exit(&statq->stats);
152 }
bch2_time_stats_quantiles_init(struct bch2_time_stats_quantiles * statq)153 static inline void bch2_time_stats_quantiles_init(struct bch2_time_stats_quantiles *statq)
154 {
155 bch2_time_stats_init(&statq->stats);
156 statq->stats.have_quantiles = true;
157 memset(&statq->quantiles, 0, sizeof(statq->quantiles));
158 }
159
160 #endif /* _BCACHEFS_TIME_STATS_H */
161