blob: 3d14163927efa638c4a24e1936fcda63d5041784 [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
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020013#include <haproxy/api.h>
Willy Tarreau66347942020-06-01 12:18:08 +020014#include <haproxy/freq_ctr.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020015#include <haproxy/time.h>
16#include <haproxy/tools.h>
Willy Tarreau7f062c42009-03-05 18:43:00 +010017
18/* Read a frequency counter taking history into account for missing time in
19 * current period. Current second is sub-divided in 1000 chunks of one ms,
20 * and the missing ones are read proportionally from previous value. The
21 * return value has the same precision as one input data sample, so low rates
22 * will be inaccurate still appropriate for max checking. One trick we use for
23 * low values is to specially handle the case where the rate is between 0 and 1
24 * in order to avoid flapping while waiting for the next event.
Willy Tarreau79584222009-03-06 09:18:27 +010025 *
26 * For immediate limit checking, it's recommended to use freq_ctr_remain() and
27 * next_event_delay() instead which do not have the flapping correction, so
28 * that even frequencies as low as one event/period are properly handled.
Willy Tarreau7f062c42009-03-05 18:43:00 +010029 */
30unsigned int read_freq_ctr(struct freq_ctr *ctr)
31{
Emeric Brun6e012862017-10-30 18:04:28 +010032 unsigned int curr, past, _curr, _past;
33 unsigned int age, curr_sec, _curr_sec;
Willy Tarreau7f062c42009-03-05 18:43:00 +010034
Willy Tarreaua06a5802017-10-31 17:54:15 +010035 while (1) {
36 _curr = ctr->curr_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020037 __ha_compiler_barrier();
Willy Tarreaua06a5802017-10-31 17:54:15 +010038 _past = ctr->prev_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020039 __ha_compiler_barrier();
Willy Tarreaufa1258f2021-04-10 23:00:53 +020040 _curr_sec = ctr->curr_tick;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020041 __ha_compiler_barrier();
Emeric Brun6e012862017-10-30 18:04:28 +010042 if (_curr_sec & 0x80000000)
43 continue;
Willy Tarreaua06a5802017-10-31 17:54:15 +010044 curr = ctr->curr_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020045 __ha_compiler_barrier();
Willy Tarreaua06a5802017-10-31 17:54:15 +010046 past = ctr->prev_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020047 __ha_compiler_barrier();
Willy Tarreaufa1258f2021-04-10 23:00:53 +020048 curr_sec = ctr->curr_tick;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020049 __ha_compiler_barrier();
Emeric Brun6e012862017-10-30 18:04:28 +010050 if (_curr == curr && _past == past && _curr_sec == curr_sec)
51 break;
52 }
Christopher Faulet94b71232017-10-12 09:49:09 +020053
Willy Tarreaua1ecbca2021-03-17 19:10:23 +010054 age = (global_now >> 32) - curr_sec;
Willy Tarreau3d8c5532009-03-06 14:29:25 +010055 if (unlikely(age > 1))
56 return 0;
57
Christopher Faulet94b71232017-10-12 09:49:09 +020058 if (unlikely(age)) {
59 past = curr;
60 curr = 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +010061 }
Willy Tarreau7f062c42009-03-05 18:43:00 +010062
Willy Tarreau3d8c5532009-03-06 14:29:25 +010063 if (past <= 1 && !curr)
64 return past; /* very low rate, avoid flapping */
65
Willy Tarreaueab777c2012-12-29 21:50:07 +010066 return curr + mul32hi(past, ms_left_scaled);
Willy Tarreau7f062c42009-03-05 18:43:00 +010067}
68
Willy Tarreau79584222009-03-06 09:18:27 +010069/* returns the number of remaining events that can occur on this freq counter
70 * while respecting <freq> and taking into account that <pend> events are
71 * already known to be pending. Returns 0 if limit was reached.
72 */
73unsigned int freq_ctr_remain(struct freq_ctr *ctr, unsigned int freq, unsigned int pend)
74{
Emeric Brun6e012862017-10-30 18:04:28 +010075 unsigned int curr, past, _curr, _past;
76 unsigned int age, curr_sec, _curr_sec;
Willy Tarreau79584222009-03-06 09:18:27 +010077
Willy Tarreaua06a5802017-10-31 17:54:15 +010078 while (1) {
79 _curr = ctr->curr_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020080 __ha_compiler_barrier();
Willy Tarreaua06a5802017-10-31 17:54:15 +010081 _past = ctr->prev_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020082 __ha_compiler_barrier();
Willy Tarreaufa1258f2021-04-10 23:00:53 +020083 _curr_sec = ctr->curr_tick;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020084 __ha_compiler_barrier();
Emeric Brun6e012862017-10-30 18:04:28 +010085 if (_curr_sec & 0x80000000)
86 continue;
Willy Tarreaua06a5802017-10-31 17:54:15 +010087 curr = ctr->curr_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020088 __ha_compiler_barrier();
Willy Tarreaua06a5802017-10-31 17:54:15 +010089 past = ctr->prev_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020090 __ha_compiler_barrier();
Willy Tarreaufa1258f2021-04-10 23:00:53 +020091 curr_sec = ctr->curr_tick;
Willy Tarreau9453ecd2020-05-28 15:29:33 +020092 __ha_compiler_barrier();
Emeric Brun6e012862017-10-30 18:04:28 +010093 if (_curr == curr && _past == past && _curr_sec == curr_sec)
94 break;
95 }
Willy Tarreau79584222009-03-06 09:18:27 +010096
Willy Tarreaua1ecbca2021-03-17 19:10:23 +010097 age = (global_now >> 32) - curr_sec;
Christopher Faulet94b71232017-10-12 09:49:09 +020098 if (unlikely(age > 1))
99 curr = 0;
100 else {
101 if (unlikely(age == 1)) {
102 past = curr;
103 curr = 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100104 }
Willy Tarreaueab777c2012-12-29 21:50:07 +0100105 curr += mul32hi(past, ms_left_scaled);
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100106 }
107 curr += pend;
108
109 if (curr >= freq)
Willy Tarreau79584222009-03-06 09:18:27 +0100110 return 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100111 return freq - curr;
Willy Tarreau79584222009-03-06 09:18:27 +0100112}
113
114/* return the expected wait time in ms before the next event may occur,
115 * respecting frequency <freq>, and assuming there may already be some pending
116 * events. It returns zero if we can proceed immediately, otherwise the wait
117 * time, which will be rounded down 1ms for better accuracy, with a minimum
118 * of one ms.
119 */
120unsigned int next_event_delay(struct freq_ctr *ctr, unsigned int freq, unsigned int pend)
121{
Emeric Brun6e012862017-10-30 18:04:28 +0100122 unsigned int curr, past, _curr, _past;
123 unsigned int wait, age, curr_sec, _curr_sec;
Willy Tarreau79584222009-03-06 09:18:27 +0100124
Willy Tarreaua06a5802017-10-31 17:54:15 +0100125 while (1) {
126 _curr = ctr->curr_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +0200127 __ha_compiler_barrier();
Willy Tarreaua06a5802017-10-31 17:54:15 +0100128 _past = ctr->prev_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +0200129 __ha_compiler_barrier();
Willy Tarreaufa1258f2021-04-10 23:00:53 +0200130 _curr_sec = ctr->curr_tick;
Willy Tarreau9453ecd2020-05-28 15:29:33 +0200131 __ha_compiler_barrier();
Emeric Brun6e012862017-10-30 18:04:28 +0100132 if (_curr_sec & 0x80000000)
133 continue;
Willy Tarreaua06a5802017-10-31 17:54:15 +0100134 curr = ctr->curr_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +0200135 __ha_compiler_barrier();
Willy Tarreaua06a5802017-10-31 17:54:15 +0100136 past = ctr->prev_ctr;
Willy Tarreau9453ecd2020-05-28 15:29:33 +0200137 __ha_compiler_barrier();
Willy Tarreaufa1258f2021-04-10 23:00:53 +0200138 curr_sec = ctr->curr_tick;
Willy Tarreau9453ecd2020-05-28 15:29:33 +0200139 __ha_compiler_barrier();
Emeric Brun6e012862017-10-30 18:04:28 +0100140 if (_curr == curr && _past == past && _curr_sec == curr_sec)
141 break;
142 }
Willy Tarreau79584222009-03-06 09:18:27 +0100143
Willy Tarreaua1ecbca2021-03-17 19:10:23 +0100144 age = (global_now >> 32) - curr_sec;
Christopher Faulet94b71232017-10-12 09:49:09 +0200145 if (unlikely(age > 1))
146 curr = 0;
147 else {
148 if (unlikely(age == 1)) {
149 past = curr;
150 curr = 0;
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100151 }
Willy Tarreaueab777c2012-12-29 21:50:07 +0100152 curr += mul32hi(past, ms_left_scaled);
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100153 }
154 curr += pend;
Willy Tarreau79584222009-03-06 09:18:27 +0100155
Willy Tarreau3d8c5532009-03-06 14:29:25 +0100156 if (curr < freq)
Willy Tarreau79584222009-03-06 09:18:27 +0100157 return 0;
158
Willy Tarreaue4d247e2021-02-09 17:39:08 +0100159 /* too many events already, let's count how long to wait before they're
160 * processed. For this we'll subtract from the number of pending events
161 * the ones programmed for the current period, to know how long to wait
162 * for the next period. Each event takes 1/freq sec, thus 1000/freq ms.
163 */
164 curr -= freq;
165 wait = curr * 1000 / (freq ? freq : 1);
Willy Tarreau79584222009-03-06 09:18:27 +0100166 return MAX(wait, 1);
167}
168
Willy Tarreauf3a9f8d2021-04-11 00:38:06 +0200169/* Returns the total number of events over the current + last period, including
170 * a number of already pending events <pend>. The average frequency will be
171 * obtained by dividing the output by <period>. This is essentially made to
172 * ease implementation of higher-level read functions.
173 *
174 * As a special case, if pend < 0, it's assumed there are no pending
175 * events and a flapping correction must be applied at the end. This is used by
176 * read_freq_ctr_period() to avoid reporting ups and downs on low-frequency
177 * events when the past value is <= 1.
178 */
Willy Tarreaufa1258f2021-04-10 23:00:53 +0200179ullong freq_ctr_total(struct freq_ctr *ctr, uint period, int pend)
Willy Tarreauf3a9f8d2021-04-11 00:38:06 +0200180{
181 ullong curr, past;
182 uint curr_tick;
183 int remain;
184
185 for (;; __ha_cpu_relax()) {
186 curr = ctr->curr_ctr;
187 past = ctr->prev_ctr;
188 curr_tick = ctr->curr_tick;
189
190 /* now let's make sure the second loads retrieve the most
191 * up-to-date values. If no value changed after a load barrier,
192 * we're certain the values we got were stable.
193 */
194 __ha_barrier_load();
195
196 if (curr_tick & 0x1)
197 continue;
198
199 if (curr != ctr->curr_ctr)
200 continue;
201
202 if (past != ctr->prev_ctr)
203 continue;
204
205 if (curr_tick != ctr->curr_tick)
206 continue;
207 break;
208 };
209
210 remain = curr_tick + period - global_now_ms;
211 if (unlikely(remain < 0)) {
212 /* We're past the first period, check if we can still report a
213 * part of last period or if we're too far away.
214 */
215 remain += period;
216 past = (remain >= 0) ? curr : 0;
217 curr = 0;
218 }
219
220 if (pend < 0) {
221 /* enable flapping correction at very low rates */
222 pend = 0;
223 if (!curr && past <= 1)
224 return past * period;
225 }
226
227 /* compute the total number of confirmed events over the period */
228 return past * remain + (curr + pend) * period;
229}
Willy Tarreau7f062c42009-03-05 18:43:00 +0100230
231/*
232 * Local variables:
233 * c-indent-level: 8
234 * c-basic-offset: 8
235 * End:
236 */