blob: a9a5869f34bea8be4d108d236ff3d188c748138f [file] [log] [blame]
Willy Tarreau7f062c42009-03-05 18:43:00 +01001/*
2 * Event rate calculation functions.
3 *
Willy Tarreau2970b0b2010-06-20 07:15:43 +02004 * Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
Willy Tarreau7f062c42009-03-05 18:43:00 +01005 *
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 <common/config.h>
14#include <common/standard.h>
15#include <common/time.h>
Willy Tarreau79584222009-03-06 09:18:27 +010016#include <common/tools.h>
Willy Tarreau7f062c42009-03-05 18:43:00 +010017#include <proto/freq_ctr.h>
18
19/* Read a frequency counter taking history into account for missing time in
20 * current period. Current second is sub-divided in 1000 chunks of one ms,
21 * and the missing ones are read proportionally from previous value. The
22 * return value has the same precision as one input data sample, so low rates
23 * will be inaccurate still appropriate for max checking. One trick we use for
24 * low values is to specially handle the case where the rate is between 0 and 1
25 * in order to avoid flapping while waiting for the next event.
Willy Tarreau79584222009-03-06 09:18:27 +010026 *
27 * For immediate limit checking, it's recommended to use freq_ctr_remain() and
28 * next_event_delay() instead which do not have the flapping correction, so
29 * that even frequencies as low as one event/period are properly handled.
Willy Tarreau7f062c42009-03-05 18:43:00 +010030 */
31unsigned int read_freq_ctr(struct freq_ctr *ctr)
32{
Emeric Brun6e012862017-10-30 18:04:28 +010033 unsigned int curr, past, _curr, _past;
34 unsigned int age, curr_sec, _curr_sec;
Willy Tarreau7f062c42009-03-05 18:43:00 +010035
Willy Tarreaua06a5802017-10-31 17:54:15 +010036 while (1) {
37 _curr = ctr->curr_ctr;
38 HA_BARRIER();
39 _past = ctr->prev_ctr;
40 HA_BARRIER();
41 _curr_sec = ctr->curr_sec;
42 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +010043 if (_curr_sec & 0x80000000)
44 continue;
Willy Tarreaua06a5802017-10-31 17:54:15 +010045 curr = ctr->curr_ctr;
46 HA_BARRIER();
47 past = ctr->prev_ctr;
48 HA_BARRIER();
49 curr_sec = ctr->curr_sec;
50 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +010051 if (_curr == curr && _past == past && _curr_sec == curr_sec)
52 break;
53 }
Christopher Faulet94b71232017-10-12 09:49:09 +020054
Willy Tarreau38deb512021-03-17 19:10:23 +010055 age = (global_now >> 32) - curr_sec;
Willy Tarreau3d8c5532009-03-06 14:29:25 +010056 if (unlikely(age > 1))
57 return 0;
58
Christopher Faulet94b71232017-10-12 09:49:09 +020059 if (unlikely(age)) {
60 past = curr;
61 curr = 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +010062 }
Willy Tarreau7f062c42009-03-05 18:43:00 +010063
Willy Tarreau3d8c5532009-03-06 14:29:25 +010064 if (past <= 1 && !curr)
65 return past; /* very low rate, avoid flapping */
66
Willy Tarreaueab777c2012-12-29 21:50:07 +010067 return curr + mul32hi(past, ms_left_scaled);
Willy Tarreau7f062c42009-03-05 18:43:00 +010068}
69
Willy Tarreau79584222009-03-06 09:18:27 +010070/* returns the number of remaining events that can occur on this freq counter
71 * while respecting <freq> and taking into account that <pend> events are
72 * already known to be pending. Returns 0 if limit was reached.
73 */
74unsigned int freq_ctr_remain(struct freq_ctr *ctr, unsigned int freq, unsigned int pend)
75{
Emeric Brun6e012862017-10-30 18:04:28 +010076 unsigned int curr, past, _curr, _past;
77 unsigned int age, curr_sec, _curr_sec;
Willy Tarreau79584222009-03-06 09:18:27 +010078
Willy Tarreaua06a5802017-10-31 17:54:15 +010079 while (1) {
80 _curr = ctr->curr_ctr;
81 HA_BARRIER();
82 _past = ctr->prev_ctr;
83 HA_BARRIER();
84 _curr_sec = ctr->curr_sec;
85 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +010086 if (_curr_sec & 0x80000000)
87 continue;
Willy Tarreaua06a5802017-10-31 17:54:15 +010088 curr = ctr->curr_ctr;
89 HA_BARRIER();
90 past = ctr->prev_ctr;
91 HA_BARRIER();
92 curr_sec = ctr->curr_sec;
93 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +010094 if (_curr == curr && _past == past && _curr_sec == curr_sec)
95 break;
96 }
Willy Tarreau79584222009-03-06 09:18:27 +010097
Willy Tarreau38deb512021-03-17 19:10:23 +010098 age = (global_now >> 32) - curr_sec;
Christopher Faulet94b71232017-10-12 09:49:09 +020099 if (unlikely(age > 1))
100 curr = 0;
101 else {
102 if (unlikely(age == 1)) {
103 past = curr;
104 curr = 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100105 }
Willy Tarreaueab777c2012-12-29 21:50:07 +0100106 curr += mul32hi(past, ms_left_scaled);
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100107 }
108 curr += pend;
109
110 if (curr >= freq)
Willy Tarreau79584222009-03-06 09:18:27 +0100111 return 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100112 return freq - curr;
Willy Tarreau79584222009-03-06 09:18:27 +0100113}
114
115/* return the expected wait time in ms before the next event may occur,
116 * respecting frequency <freq>, and assuming there may already be some pending
117 * events. It returns zero if we can proceed immediately, otherwise the wait
118 * time, which will be rounded down 1ms for better accuracy, with a minimum
119 * of one ms.
120 */
121unsigned int next_event_delay(struct freq_ctr *ctr, unsigned int freq, unsigned int pend)
122{
Emeric Brun6e012862017-10-30 18:04:28 +0100123 unsigned int curr, past, _curr, _past;
124 unsigned int wait, age, curr_sec, _curr_sec;
Willy Tarreau79584222009-03-06 09:18:27 +0100125
Willy Tarreaua06a5802017-10-31 17:54:15 +0100126 while (1) {
127 _curr = ctr->curr_ctr;
128 HA_BARRIER();
129 _past = ctr->prev_ctr;
130 HA_BARRIER();
131 _curr_sec = ctr->curr_sec;
132 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100133 if (_curr_sec & 0x80000000)
134 continue;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100135 curr = ctr->curr_ctr;
136 HA_BARRIER();
137 past = ctr->prev_ctr;
138 HA_BARRIER();
139 curr_sec = ctr->curr_sec;
140 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100141 if (_curr == curr && _past == past && _curr_sec == curr_sec)
142 break;
143 }
Willy Tarreau79584222009-03-06 09:18:27 +0100144
Willy Tarreau38deb512021-03-17 19:10:23 +0100145 age = (global_now >> 32) - curr_sec;
Christopher Faulet94b71232017-10-12 09:49:09 +0200146 if (unlikely(age > 1))
147 curr = 0;
148 else {
149 if (unlikely(age == 1)) {
150 past = curr;
151 curr = 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100152 }
Willy Tarreaueab777c2012-12-29 21:50:07 +0100153 curr += mul32hi(past, ms_left_scaled);
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100154 }
155 curr += pend;
Willy Tarreau79584222009-03-06 09:18:27 +0100156
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100157 if (curr < freq)
Willy Tarreau79584222009-03-06 09:18:27 +0100158 return 0;
159
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100160 wait = 999 / curr;
Willy Tarreau79584222009-03-06 09:18:27 +0100161 return MAX(wait, 1);
162}
163
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200164/* Reads a frequency counter taking history into account for missing time in
165 * current period. The period has to be passed in number of ticks and must
166 * match the one used to feed the counter. The counter value is reported for
Willy Tarreau38deb512021-03-17 19:10:23 +0100167 * current global date. The return value has the same precision as one input
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200168 * data sample, so low rates over the period will be inaccurate but still
169 * appropriate for max checking. One trick we use for low values is to specially
170 * handle the case where the rate is between 0 and 1 in order to avoid flapping
171 * while waiting for the next event.
172 *
173 * For immediate limit checking, it's recommended to use freq_ctr_period_remain()
174 * instead which does not have the flapping correction, so that even frequencies
175 * as low as one event/period are properly handled.
176 *
177 * For measures over a 1-second period, it's better to use the implicit functions
178 * above.
179 */
180unsigned int read_freq_ctr_period(struct freq_ctr_period *ctr, unsigned int period)
181{
Emeric Brun6e012862017-10-30 18:04:28 +0100182 unsigned int _curr, _past, curr, past;
183 unsigned int remain, _curr_tick, curr_tick;
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200184
Willy Tarreaua06a5802017-10-31 17:54:15 +0100185 while (1) {
Emeric Brun6e012862017-10-30 18:04:28 +0100186 _curr = ctr->curr_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100187 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100188 _past = ctr->prev_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100189 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100190 _curr_tick = ctr->curr_tick;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100191 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100192 if (_curr_tick & 0x1)
193 continue;
Christopher Faulet94b71232017-10-12 09:49:09 +0200194 curr = ctr->curr_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100195 HA_BARRIER();
Christopher Faulet94b71232017-10-12 09:49:09 +0200196 past = ctr->prev_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100197 HA_BARRIER();
Christopher Faulet94b71232017-10-12 09:49:09 +0200198 curr_tick = ctr->curr_tick;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100199 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100200 if (_curr == curr && _past == past && _curr_tick == curr_tick)
201 break;
202 };
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200203
Willy Tarreau146c87c2021-03-23 08:58:22 +0100204 remain = curr_tick + period - global_now_ms;
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200205 if (unlikely((int)remain < 0)) {
206 /* We're past the first period, check if we can still report a
207 * part of last period or if we're too far away.
208 */
209 remain += period;
210 if ((int)remain < 0)
211 return 0;
212 past = curr;
213 curr = 0;
214 }
215 if (past <= 1 && !curr)
216 return past; /* very low rate, avoid flapping */
217
218 curr += div64_32((unsigned long long)past * remain, period);
219 return curr;
220}
221
222/* Returns the number of remaining events that can occur on this freq counter
223 * while respecting <freq> events per period, and taking into account that
224 * <pend> events are already known to be pending. Returns 0 if limit was reached.
225 */
226unsigned int freq_ctr_remain_period(struct freq_ctr_period *ctr, unsigned int period,
227 unsigned int freq, unsigned int pend)
228{
Emeric Brun6e012862017-10-30 18:04:28 +0100229 unsigned int _curr, _past, curr, past;
230 unsigned int remain, _curr_tick, curr_tick;
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200231
Willy Tarreaua06a5802017-10-31 17:54:15 +0100232 while (1) {
Emeric Brun6e012862017-10-30 18:04:28 +0100233 _curr = ctr->curr_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100234 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100235 _past = ctr->prev_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100236 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100237 _curr_tick = ctr->curr_tick;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100238 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100239 if (_curr_tick & 0x1)
240 continue;
Christopher Faulet94b71232017-10-12 09:49:09 +0200241 curr = ctr->curr_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100242 HA_BARRIER();
Christopher Faulet94b71232017-10-12 09:49:09 +0200243 past = ctr->prev_ctr;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100244 HA_BARRIER();
Christopher Faulet94b71232017-10-12 09:49:09 +0200245 curr_tick = ctr->curr_tick;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100246 HA_BARRIER();
Emeric Brun6e012862017-10-30 18:04:28 +0100247 if (_curr == curr && _past == past && _curr_tick == curr_tick)
248 break;
249 };
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200250
Willy Tarreau146c87c2021-03-23 08:58:22 +0100251 remain = curr_tick + period - global_now_ms;
Willy Tarreau2970b0b2010-06-20 07:15:43 +0200252 if (likely((int)remain < 0)) {
253 /* We're past the first period, check if we can still report a
254 * part of last period or if we're too far away.
255 */
256 past = curr;
257 curr = 0;
258 remain += period;
259 if ((int)remain < 0)
260 past = 0;
261 }
262 if (likely(past))
263 curr += div64_32((unsigned long long)past * remain, period);
264
265 curr += pend;
266 freq -= curr;
267 if ((int)freq < 0)
268 freq = 0;
269 return freq;
270}
271
Willy Tarreau7f062c42009-03-05 18:43:00 +0100272
273/*
274 * Local variables:
275 * c-indent-level: 8
276 * c-basic-offset: 8
277 * End:
278 */