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 30 #include "mean_and_variance.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 *bch2_pick_time_units(u64 ns); 41 42 /* 43 * quantiles - do not use: 44 * 45 * Only enabled if bch2_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[32]; 67 }; 68 69 struct bch2_time_stats { 70 spinlock_t lock; 71 bool quantiles_enabled; 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 struct quantiles quantiles; 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 void __bch2_time_stats_clear_buffer(struct bch2_time_stats *, struct time_stat_buffer *); 94 void __bch2_time_stats_update(struct bch2_time_stats *stats, u64, u64); 95 96 /** 97 * time_stats_update - collect a new event being tracked 98 * 99 * @stats - bch2_time_stats to update 100 * @start - start time of event, recorded with local_clock() 101 * 102 * The end duration of the event will be the current time 103 */ 104 static inline void bch2_time_stats_update(struct bch2_time_stats *stats, u64 start) 105 { 106 __bch2_time_stats_update(stats, start, local_clock()); 107 } 108 109 /** 110 * track_event_change - track state change events 111 * 112 * @stats - bch2_time_stats to update 113 * @v - new state, true or false 114 * 115 * Use this when tracking time stats for state changes, i.e. resource X becoming 116 * blocked/unblocked. 117 */ 118 static inline bool track_event_change(struct bch2_time_stats *stats, bool v) 119 { 120 if (v != !!stats->last_event_start) { 121 if (!v) { 122 bch2_time_stats_update(stats, stats->last_event_start); 123 stats->last_event_start = 0; 124 } else { 125 stats->last_event_start = local_clock() ?: 1; 126 return true; 127 } 128 } 129 130 return false; 131 } 132 133 void bch2_time_stats_exit(struct bch2_time_stats *); 134 void bch2_time_stats_init(struct bch2_time_stats *); 135 136 #endif /* _BCACHEFS_TIME_STATS_H */ 137