]> git.sesse.net Git - bcachefs-tools-debian/blob - include/linux/time_stats.h
Disable pristine-tar option in gbp.conf, since there is no pristine-tar branch.
[bcachefs-tools-debian] / include / linux / time_stats.h
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * 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 _LINUX_TIME_STATS_H
25 #define _LINUX_TIME_STATS_H
26
27 #include <linux/mean_and_variance.h>
28 #include <linux/sched/clock.h>
29 #include <linux/spinlock_types.h>
30 #include <linux/string.h>
31
32 struct time_unit {
33         const char      *name;
34         u64             nsecs;
35 };
36
37 /*
38  * given a nanosecond value, pick the preferred time units for printing:
39  */
40 const struct time_unit *pick_time_units(u64 ns);
41
42 /*
43  * quantiles - do not use:
44  *
45  * Only enabled if time_stats->quantiles_enabled has been manually set - don't
46  * use in new code.
47  */
48
49 #define NR_QUANTILES    15
50 #define QUANTILE_IDX(i) inorder_to_eytzinger0(i, NR_QUANTILES)
51 #define QUANTILE_FIRST  eytzinger0_first(NR_QUANTILES)
52 #define QUANTILE_LAST   eytzinger0_last(NR_QUANTILES)
53
54 struct quantiles {
55         struct quantile_entry {
56                 u64     m;
57                 u64     step;
58         }               entries[NR_QUANTILES];
59 };
60
61 struct time_stat_buffer {
62         unsigned        nr;
63         struct time_stat_buffer_entry {
64                 u64     start;
65                 u64     end;
66         }               entries[31];
67 };
68
69 struct time_stats {
70         spinlock_t      lock;
71         bool            have_quantiles;
72         /* all fields are in nanoseconds */
73         u64             min_duration;
74         u64             max_duration;
75         u64             total_duration;
76         u64             max_freq;
77         u64             min_freq;
78         u64             last_event;
79         u64             last_event_start;
80
81         struct mean_and_variance          duration_stats;
82         struct mean_and_variance          freq_stats;
83
84 /* default weight for weighted mean and variance calculations */
85 #define TIME_STATS_MV_WEIGHT    8
86
87         struct mean_and_variance_weighted duration_stats_weighted;
88         struct mean_and_variance_weighted freq_stats_weighted;
89         struct time_stat_buffer __percpu *buffer;
90
91         u64             start_time;
92 };
93
94 struct time_stats_quantiles {
95         struct time_stats       stats;
96         struct quantiles        quantiles;
97 };
98
99 static inline struct quantiles *time_stats_to_quantiles(struct time_stats *stats)
100 {
101         return stats->have_quantiles
102                 ? &container_of(stats, struct time_stats_quantiles, stats)->quantiles
103                 : NULL;
104 }
105
106 void __time_stats_clear_buffer(struct time_stats *, struct time_stat_buffer *);
107 void __time_stats_update(struct time_stats *stats, u64, u64);
108
109 /**
110  * time_stats_update - collect a new event being tracked
111  *
112  * @stats       - time_stats to update
113  * @start       - start time of event, recorded with local_clock()
114  *
115  * The end duration of the event will be the current time
116  */
117 static inline void time_stats_update(struct time_stats *stats, u64 start)
118 {
119         __time_stats_update(stats, start, local_clock());
120 }
121
122 /**
123  * track_event_change - track state change events
124  *
125  * @stats       - time_stats to update
126  * @v           - new state, true or false
127  *
128  * Use this when tracking time stats for state changes, i.e. resource X becoming
129  * blocked/unblocked.
130  */
131 static inline bool track_event_change(struct time_stats *stats, bool v)
132 {
133         if (v != !!stats->last_event_start) {
134                 if (!v) {
135                         time_stats_update(stats, stats->last_event_start);
136                         stats->last_event_start = 0;
137                 } else {
138                         stats->last_event_start = local_clock() ?: 1;
139                         return true;
140                 }
141         }
142
143         return false;
144 }
145
146 #define TIME_STATS_PRINT_NO_ZEROES      (1U << 0)       /* print nothing if zero count */
147 struct seq_buf;
148 void time_stats_to_seq_buf(struct seq_buf *, struct time_stats *,
149                 const char *epoch_name, unsigned int flags);
150 void time_stats_to_json(struct seq_buf *, struct time_stats *,
151                 const char *epoch_name, unsigned int flags);
152
153 void time_stats_exit(struct time_stats *);
154 void time_stats_init(struct time_stats *);
155
156 static inline void time_stats_quantiles_exit(struct time_stats_quantiles *statq)
157 {
158         time_stats_exit(&statq->stats);
159 }
160 static inline void time_stats_quantiles_init(struct time_stats_quantiles *statq)
161 {
162         time_stats_init(&statq->stats);
163         statq->stats.have_quantiles = true;
164         memset(&statq->quantiles, 0, sizeof(statq->quantiles));
165 }
166
167 #endif /* _LINUX_TIME_STATS_H */