/src/haproxy/src/freq_ctr.c
Line | Count | Source |
1 | | /* |
2 | | * Event rate calculation functions. |
3 | | * |
4 | | * Copyright 2000-2010 Willy Tarreau <w@1wt.eu> |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU General Public License |
8 | | * as published by the Free Software Foundation; either version |
9 | | * 2 of the License, or (at your option) any later version. |
10 | | * |
11 | | */ |
12 | | |
13 | | #include <haproxy/api.h> |
14 | | #include <haproxy/freq_ctr.h> |
15 | | #include <haproxy/tools.h> |
16 | | |
17 | | /* Update a frequency counter by <inc> incremental units. It is automatically |
18 | | * rotated if the period is over. It is important that it correctly initializes |
19 | | * a null area. This one works on frequency counters which have a period |
20 | | * different from one second. It relies on the process-wide clock that is |
21 | | * guaranteed to be monotonic. It's important to avoid forced rotates between |
22 | | * threads. A faster wrapper (update_freq_ctr_period) should be used instead, |
23 | | * which uses the thread's local time whenever possible and falls back to this |
24 | | * one when needed (less than 0.003% of the time). |
25 | | */ |
26 | | uint update_freq_ctr_period_slow(struct freq_ctr *ctr, uint period, uint inc) |
27 | 0 | { |
28 | 0 | uint curr_tick; |
29 | 0 | uint32_t now_ms_tmp; |
30 | | |
31 | | /* atomically update the counter if still within the period, even if |
32 | | * a rotation is in progress (no big deal). |
33 | | */ |
34 | 0 | for (;; __ha_cpu_relax()) { |
35 | 0 | curr_tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
36 | 0 | now_ms_tmp = HA_ATOMIC_LOAD(global_now_ms); |
37 | |
|
38 | 0 | if (now_ms_tmp - curr_tick < period) |
39 | 0 | return HA_ATOMIC_ADD_FETCH(&ctr->curr_ctr, inc); |
40 | | |
41 | | /* a rotation is needed. While extremely rare, contention may |
42 | | * happen because it will be triggered on time, and all threads |
43 | | * see the time change simultaneously. |
44 | | */ |
45 | 0 | if (!(curr_tick & 1) && |
46 | 0 | HA_ATOMIC_CAS(&ctr->curr_tick, &curr_tick, curr_tick | 0x1)) |
47 | 0 | break; |
48 | 0 | } |
49 | | |
50 | | /* atomically switch the new period into the old one without losing any |
51 | | * potential concurrent update. We're the only one performing the rotate |
52 | | * (locked above), others are only adding positive values to curr_ctr. |
53 | | */ |
54 | 0 | HA_ATOMIC_STORE(&ctr->prev_ctr, HA_ATOMIC_XCHG(&ctr->curr_ctr, inc)); |
55 | 0 | curr_tick += period; |
56 | 0 | if (likely(now_ms_tmp - curr_tick >= period)) { |
57 | | /* we missed at least two periods */ |
58 | 0 | HA_ATOMIC_STORE(&ctr->prev_ctr, 0); |
59 | 0 | curr_tick = now_ms_tmp; |
60 | 0 | } |
61 | | |
62 | | /* release the lock and update the time in case of rotate. */ |
63 | 0 | HA_ATOMIC_STORE(&ctr->curr_tick, curr_tick & ~1); |
64 | 0 | return inc; |
65 | 0 | } |
66 | | |
67 | | /* Returns the total number of events over the current + last period, including |
68 | | * a number of already pending events <pend>. The average frequency will be |
69 | | * obtained by dividing the output by <period>. This is essentially made to |
70 | | * ease implementation of higher-level read functions. This function does not |
71 | | * access the freq_ctr itself, it's supposed to be called with the stabilized |
72 | | * values. See freq_ctr_total() and freq_ctr_total_estimate() instead. |
73 | | * |
74 | | * As a special case, if pend < 0, it's assumed there are no pending |
75 | | * events and a flapping correction must be applied at the end. This is used by |
76 | | * read_freq_ctr_period() to avoid reporting ups and downs on low-frequency |
77 | | * events when the past value is <= 1. |
78 | | */ |
79 | | ullong _freq_ctr_total_from_values(uint period, int pend, |
80 | | uint tick, ullong past, ullong curr) |
81 | 0 | { |
82 | 0 | int remain; |
83 | |
|
84 | 0 | remain = tick + period - HA_ATOMIC_LOAD(global_now_ms); |
85 | 0 | if (unlikely(remain < 0)) { |
86 | | /* We're past the first period, check if we can still report a |
87 | | * part of last period or if we're too far away. |
88 | | */ |
89 | 0 | remain += period; |
90 | 0 | past = (remain >= 0) ? curr : 0; |
91 | 0 | curr = 0; |
92 | 0 | } |
93 | |
|
94 | 0 | if (pend < 0) { |
95 | | /* enable flapping correction at very low rates */ |
96 | 0 | pend = 0; |
97 | 0 | if (!curr && past <= 1) |
98 | 0 | return past * period; |
99 | 0 | } |
100 | | |
101 | | /* compute the total number of confirmed events over the period */ |
102 | 0 | return past * remain + (curr + pend) * period; |
103 | 0 | } |
104 | | |
105 | | /* Returns the total number of events over the current + last period, including |
106 | | * a number of already pending events <pend>. The average frequency will be |
107 | | * obtained by dividing the output by <period>. This is essentially made to |
108 | | * ease implementation of higher-level read functions. |
109 | | * |
110 | | * As a special case, if pend < 0, it's assumed there are no pending |
111 | | * events and a flapping correction must be applied at the end. This is used by |
112 | | * read_freq_ctr_period() to avoid reporting ups and downs on low-frequency |
113 | | * events when the past value is <= 1. |
114 | | */ |
115 | | ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend) |
116 | 0 | { |
117 | 0 | ullong curr, past, old_curr, old_past; |
118 | 0 | uint tick, old_tick; |
119 | |
|
120 | 0 | tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
121 | 0 | curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); |
122 | 0 | past = HA_ATOMIC_LOAD(&ctr->prev_ctr); |
123 | |
|
124 | 0 | while (1) { |
125 | 0 | if (tick & 0x1) // change in progress |
126 | 0 | goto redo0; |
127 | | |
128 | 0 | old_tick = tick; |
129 | 0 | old_curr = curr; |
130 | 0 | old_past = past; |
131 | | |
132 | | /* now let's load the values a second time and make sure they |
133 | | * did not change, which will indicate it was a stable reading. |
134 | | */ |
135 | |
|
136 | 0 | tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
137 | 0 | if (tick & 0x1) // change in progress |
138 | 0 | goto redo0; |
139 | | |
140 | 0 | if (tick != old_tick) |
141 | 0 | goto redo1; |
142 | | |
143 | 0 | curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); |
144 | 0 | if (curr != old_curr) |
145 | 0 | goto redo2; |
146 | | |
147 | 0 | past = HA_ATOMIC_LOAD(&ctr->prev_ctr); |
148 | 0 | if (past != old_past) |
149 | 0 | goto redo3; |
150 | | |
151 | | /* all values match between two loads, they're stable, let's |
152 | | * quit now. |
153 | | */ |
154 | 0 | break; |
155 | 0 | redo0: |
156 | 0 | tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
157 | 0 | redo1: |
158 | 0 | curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); |
159 | 0 | redo2: |
160 | 0 | past = HA_ATOMIC_LOAD(&ctr->prev_ctr); |
161 | 0 | redo3: |
162 | 0 | __ha_cpu_relax(); |
163 | 0 | }; |
164 | 0 | return _freq_ctr_total_from_values(period, pend, tick, past, curr); |
165 | 0 | } |
166 | | |
167 | | /* Like the function above but doesn't block if the entry is locked. In this |
168 | | * case it will only return the most accurate estimate it can bring. Based on |
169 | | * the update order in update_freq_ctr_period_slow() above, it may return a |
170 | | * low value caused by the replacement of the curr value before the past one |
171 | | * and/or the tick was updated. Otherwise the value will be correct most of |
172 | | * the time. This is only meant to be used from debug handlers. |
173 | | */ |
174 | | ullong freq_ctr_total_estimate(const struct freq_ctr *ctr, uint period, int pend) |
175 | 0 | { |
176 | 0 | ullong curr, past; |
177 | 0 | uint tick; |
178 | |
|
179 | 0 | tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
180 | 0 | curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); |
181 | 0 | past = HA_ATOMIC_LOAD(&ctr->prev_ctr); |
182 | |
|
183 | 0 | tick &= ~1; |
184 | 0 | return _freq_ctr_total_from_values(period, pend, tick, past, curr); |
185 | 0 | } |
186 | | |
187 | | /* Returns the excess of events over the current period for target frequency |
188 | | * <freq>. It returns 0 if the counter is in the future or if the counter is |
189 | | * empty. The result considers the position of the current time within the |
190 | | * current period. |
191 | | * |
192 | | * The caller may safely add new events if result is zero. |
193 | | */ |
194 | | uint freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq) |
195 | 0 | { |
196 | 0 | ullong curr, old_curr; |
197 | 0 | uint tick, old_tick, linear_usage; |
198 | 0 | int elapsed; |
199 | |
|
200 | 0 | tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
201 | 0 | curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); |
202 | |
|
203 | 0 | while (1) { |
204 | 0 | if (tick & 0x1) // change in progress |
205 | 0 | goto redo0; |
206 | | |
207 | 0 | old_tick = tick; |
208 | 0 | old_curr = curr; |
209 | | |
210 | | /* now let's load the values a second time and make sure they |
211 | | * did not change, which will indicate it was a stable reading. |
212 | | */ |
213 | |
|
214 | 0 | tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
215 | 0 | if (tick & 0x1) // change in progress |
216 | 0 | goto redo0; |
217 | | |
218 | 0 | if (tick != old_tick) |
219 | 0 | goto redo1; |
220 | | |
221 | 0 | curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); |
222 | 0 | if (curr != old_curr) |
223 | 0 | goto redo2; |
224 | | |
225 | | /* all values match between two loads, they're stable, let's |
226 | | * quit now. |
227 | | */ |
228 | 0 | break; |
229 | 0 | redo0: |
230 | 0 | tick = HA_ATOMIC_LOAD(&ctr->curr_tick); |
231 | 0 | redo1: |
232 | 0 | curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); |
233 | 0 | redo2: |
234 | 0 | __ha_cpu_relax(); |
235 | 0 | }; |
236 | |
|
237 | 0 | if (!curr && !tick) { |
238 | | /* The counter is empty, there is no overshoot */ |
239 | 0 | return 0; |
240 | 0 | } |
241 | | |
242 | 0 | elapsed = HA_ATOMIC_LOAD(global_now_ms) - tick; |
243 | 0 | if (unlikely(elapsed < 0 || elapsed > period)) { |
244 | | /* The counter is in the future or the elapsed time is higher than the period, there is no overshoot */ |
245 | 0 | return 0; |
246 | 0 | } |
247 | | |
248 | 0 | linear_usage = div64_32((uint64_t)elapsed * freq, period); |
249 | 0 | if (curr < linear_usage) { |
250 | | /* The counter is below a uniform linear increase across the period, no overshoot */ |
251 | 0 | return 0; |
252 | 0 | } |
253 | 0 | return curr - linear_usage; |
254 | 0 | } |
255 | | |
256 | | /* |
257 | | * Local variables: |
258 | | * c-indent-level: 8 |
259 | | * c-basic-offset: 8 |
260 | | * End: |
261 | | */ |