Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Floating proportions with flexible aging period
4 : : *
5 : : * Copyright (C) 2011, SUSE, Jan Kara <jack@suse.cz>
6 : : *
7 : : * The goal of this code is: Given different types of event, measure proportion
8 : : * of each type of event over time. The proportions are measured with
9 : : * exponentially decaying history to give smooth transitions. A formula
10 : : * expressing proportion of event of type 'j' is:
11 : : *
12 : : * p_{j} = (\Sum_{i>=0} x_{i,j}/2^{i+1})/(\Sum_{i>=0} x_i/2^{i+1})
13 : : *
14 : : * Where x_{i,j} is j's number of events in i-th last time period and x_i is
15 : : * total number of events in i-th last time period.
16 : : *
17 : : * Note that p_{j}'s are normalised, i.e.
18 : : *
19 : : * \Sum_{j} p_{j} = 1,
20 : : *
21 : : * This formula can be straightforwardly computed by maintaining denominator
22 : : * (let's call it 'd') and for each event type its numerator (let's call it
23 : : * 'n_j'). When an event of type 'j' happens, we simply need to do:
24 : : * n_j++; d++;
25 : : *
26 : : * When a new period is declared, we could do:
27 : : * d /= 2
28 : : * for each j
29 : : * n_j /= 2
30 : : *
31 : : * To avoid iteration over all event types, we instead shift numerator of event
32 : : * j lazily when someone asks for a proportion of event j or when event j
33 : : * occurs. This can bit trivially implemented by remembering last period in
34 : : * which something happened with proportion of type j.
35 : : */
36 : : #include <linux/flex_proportions.h>
37 : :
38 : 11 : int fprop_global_init(struct fprop_global *p, gfp_t gfp)
39 : : {
40 : 11 : int err;
41 : :
42 : 11 : p->period = 0;
43 : : /* Use 1 to avoid dealing with periods with 0 events... */
44 : 11 : err = percpu_counter_init(&p->events, 1, gfp);
45 [ + - ]: 11 : if (err)
46 : : return err;
47 : 11 : seqcount_init(&p->sequence);
48 : 11 : return 0;
49 : : }
50 : :
51 : 0 : void fprop_global_destroy(struct fprop_global *p)
52 : : {
53 : 0 : percpu_counter_destroy(&p->events);
54 : 0 : }
55 : :
56 : : /*
57 : : * Declare @periods new periods. It is upto the caller to make sure period
58 : : * transitions cannot happen in parallel.
59 : : *
60 : : * The function returns true if the proportions are still defined and false
61 : : * if aging zeroed out all events. This can be used to detect whether declaring
62 : : * further periods has any effect.
63 : : */
64 : 11 : bool fprop_new_period(struct fprop_global *p, int periods)
65 : : {
66 : 11 : s64 events;
67 : 11 : unsigned long flags;
68 : :
69 : 11 : local_irq_save(flags);
70 : 11 : events = percpu_counter_sum(&p->events);
71 : : /*
72 : : * Don't do anything if there are no events.
73 : : */
74 [ - + ]: 11 : if (events <= 1) {
75 : 0 : local_irq_restore(flags);
76 : 0 : return false;
77 : : }
78 : 11 : write_seqcount_begin(&p->sequence);
79 [ + - ]: 11 : if (periods < 64)
80 : 11 : events -= events >> periods;
81 : : /* Use addition to avoid losing events happening between sum and set */
82 : 11 : percpu_counter_add(&p->events, -events);
83 : 11 : p->period += periods;
84 : 11 : write_seqcount_end(&p->sequence);
85 : 11 : local_irq_restore(flags);
86 : :
87 : 11 : return true;
88 : : }
89 : :
90 : : /*
91 : : * ---- SINGLE ----
92 : : */
93 : :
94 : 0 : int fprop_local_init_single(struct fprop_local_single *pl)
95 : : {
96 : 0 : pl->events = 0;
97 : 0 : pl->period = 0;
98 : 0 : raw_spin_lock_init(&pl->lock);
99 : 0 : return 0;
100 : : }
101 : :
102 : 0 : void fprop_local_destroy_single(struct fprop_local_single *pl)
103 : : {
104 : 0 : }
105 : :
106 : : static void fprop_reflect_period_single(struct fprop_global *p,
107 : : struct fprop_local_single *pl)
108 : : {
109 : : unsigned int period = p->period;
110 : : unsigned long flags;
111 : :
112 : : /* Fast path - period didn't change */
113 : : if (pl->period == period)
114 : : return;
115 : : raw_spin_lock_irqsave(&pl->lock, flags);
116 : : /* Someone updated pl->period while we were spinning? */
117 : : if (pl->period >= period) {
118 : : raw_spin_unlock_irqrestore(&pl->lock, flags);
119 : : return;
120 : : }
121 : : /* Aging zeroed our fraction? */
122 : : if (period - pl->period < BITS_PER_LONG)
123 : : pl->events >>= period - pl->period;
124 : : else
125 : : pl->events = 0;
126 : : pl->period = period;
127 : : raw_spin_unlock_irqrestore(&pl->lock, flags);
128 : : }
129 : :
130 : : /* Event of type pl happened */
131 : 0 : void __fprop_inc_single(struct fprop_global *p, struct fprop_local_single *pl)
132 : : {
133 : 0 : fprop_reflect_period_single(p, pl);
134 : 0 : pl->events++;
135 : 0 : percpu_counter_add(&p->events, 1);
136 : 0 : }
137 : :
138 : : /* Return fraction of events of type pl */
139 : 0 : void fprop_fraction_single(struct fprop_global *p,
140 : : struct fprop_local_single *pl,
141 : : unsigned long *numerator, unsigned long *denominator)
142 : : {
143 : 0 : unsigned int seq;
144 : 0 : s64 num, den;
145 : :
146 : 0 : do {
147 : 0 : seq = read_seqcount_begin(&p->sequence);
148 : 0 : fprop_reflect_period_single(p, pl);
149 : 0 : num = pl->events;
150 : 0 : den = percpu_counter_read_positive(&p->events);
151 [ # # ]: 0 : } while (read_seqcount_retry(&p->sequence, seq));
152 : :
153 : : /*
154 : : * Make fraction <= 1 and denominator > 0 even in presence of percpu
155 : : * counter errors
156 : : */
157 [ # # ]: 0 : if (den <= num) {
158 [ # # ]: 0 : if (num)
159 : : den = num;
160 : : else
161 : 0 : den = 1;
162 : : }
163 : 0 : *denominator = den;
164 : 0 : *numerator = num;
165 : 0 : }
166 : :
167 : : /*
168 : : * ---- PERCPU ----
169 : : */
170 : : #define PROP_BATCH (8*(1+ilog2(nr_cpu_ids)))
171 : :
172 : 143 : int fprop_local_init_percpu(struct fprop_local_percpu *pl, gfp_t gfp)
173 : : {
174 : 143 : int err;
175 : :
176 : 143 : err = percpu_counter_init(&pl->events, 0, gfp);
177 [ + - ]: 143 : if (err)
178 : : return err;
179 : 143 : pl->period = 0;
180 : 143 : raw_spin_lock_init(&pl->lock);
181 : 143 : return 0;
182 : : }
183 : :
184 : 0 : void fprop_local_destroy_percpu(struct fprop_local_percpu *pl)
185 : : {
186 : 0 : percpu_counter_destroy(&pl->events);
187 : 0 : }
188 : :
189 : : static void fprop_reflect_period_percpu(struct fprop_global *p,
190 : : struct fprop_local_percpu *pl)
191 : : {
192 : : unsigned int period = p->period;
193 : : unsigned long flags;
194 : :
195 : : /* Fast path - period didn't change */
196 : : if (pl->period == period)
197 : : return;
198 : : raw_spin_lock_irqsave(&pl->lock, flags);
199 : : /* Someone updated pl->period while we were spinning? */
200 : : if (pl->period >= period) {
201 : : raw_spin_unlock_irqrestore(&pl->lock, flags);
202 : : return;
203 : : }
204 : : /* Aging zeroed our fraction? */
205 : : if (period - pl->period < BITS_PER_LONG) {
206 : : s64 val = percpu_counter_read(&pl->events);
207 : :
208 : : if (val < (nr_cpu_ids * PROP_BATCH))
209 : : val = percpu_counter_sum(&pl->events);
210 : :
211 : : percpu_counter_add_batch(&pl->events,
212 : : -val + (val >> (period-pl->period)), PROP_BATCH);
213 : : } else
214 : : percpu_counter_set(&pl->events, 0);
215 : : pl->period = period;
216 : : raw_spin_unlock_irqrestore(&pl->lock, flags);
217 : : }
218 : :
219 : : /* Event of type pl happened */
220 : 0 : void __fprop_inc_percpu(struct fprop_global *p, struct fprop_local_percpu *pl)
221 : : {
222 : 0 : fprop_reflect_period_percpu(p, pl);
223 [ # # # # : 0 : percpu_counter_add_batch(&pl->events, 1, PROP_BATCH);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
224 : 0 : percpu_counter_add(&p->events, 1);
225 : 0 : }
226 : :
227 : 0 : void fprop_fraction_percpu(struct fprop_global *p,
228 : : struct fprop_local_percpu *pl,
229 : : unsigned long *numerator, unsigned long *denominator)
230 : : {
231 : 0 : unsigned int seq;
232 : 0 : s64 num, den;
233 : :
234 : 0 : do {
235 : 0 : seq = read_seqcount_begin(&p->sequence);
236 : 0 : fprop_reflect_period_percpu(p, pl);
237 : 0 : num = percpu_counter_read_positive(&pl->events);
238 : 0 : den = percpu_counter_read_positive(&p->events);
239 [ # # ]: 0 : } while (read_seqcount_retry(&p->sequence, seq));
240 : :
241 : : /*
242 : : * Make fraction <= 1 and denominator > 0 even in presence of percpu
243 : : * counter errors
244 : : */
245 [ # # ]: 0 : if (den <= num) {
246 [ # # ]: 0 : if (num)
247 : : den = num;
248 : : else
249 : 0 : den = 1;
250 : : }
251 : 0 : *denominator = den;
252 : 0 : *numerator = num;
253 : 0 : }
254 : :
255 : : /*
256 : : * Like __fprop_inc_percpu() except that event is counted only if the given
257 : : * type has fraction smaller than @max_frac/FPROP_FRAC_BASE
258 : : */
259 : 88 : void __fprop_inc_percpu_max(struct fprop_global *p,
260 : : struct fprop_local_percpu *pl, int max_frac)
261 : : {
262 [ - + ]: 88 : if (unlikely(max_frac < FPROP_FRAC_BASE)) {
263 : 0 : unsigned long numerator, denominator;
264 : :
265 : 0 : fprop_fraction_percpu(p, pl, &numerator, &denominator);
266 : 0 : if (numerator >
267 [ # # ]: 0 : (((u64)denominator) * max_frac) >> FPROP_FRAC_SHIFT)
268 : 0 : return;
269 : : } else
270 : 88 : fprop_reflect_period_percpu(p, pl);
271 [ - + - - : 88 : percpu_counter_add_batch(&pl->events, 1, PROP_BATCH);
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - - -
- - - -
- ]
272 : 88 : percpu_counter_add(&p->events, 1);
273 : : }
|