GNU Linux-libre 6.9.1-gnu
[releases.git] / fs / bcachefs / time_stats.h
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         /* all fields are in nanoseconds */
74         u64             min_duration;
75         u64             max_duration;
76         u64             total_duration;
77         u64             max_freq;
78         u64             min_freq;
79         u64             last_event;
80         u64             last_event_start;
81
82         struct mean_and_variance          duration_stats;
83         struct mean_and_variance          freq_stats;
84
85 /* default weight for weighted mean and variance calculations */
86 #define TIME_STATS_MV_WEIGHT    8
87
88         struct mean_and_variance_weighted duration_stats_weighted;
89         struct mean_and_variance_weighted freq_stats_weighted;
90         struct time_stat_buffer __percpu *buffer;
91 };
92
93 struct bch2_time_stats_quantiles {
94         struct bch2_time_stats  stats;
95         struct quantiles        quantiles;
96 };
97
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  */
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  */
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_exit(struct bch2_time_stats *);
146 void bch2_time_stats_init(struct bch2_time_stats *);
147
148 static inline void bch2_time_stats_quantiles_exit(struct bch2_time_stats_quantiles *statq)
149 {
150         bch2_time_stats_exit(&statq->stats);
151 }
152 static inline void bch2_time_stats_quantiles_init(struct bch2_time_stats_quantiles *statq)
153 {
154         bch2_time_stats_init(&statq->stats);
155         statq->stats.have_quantiles = true;
156         memset(&statq->quantiles, 0, sizeof(statq->quantiles));
157 }
158
159 #endif /* _BCACHEFS_TIME_STATS_H */