From aa55640b8c3704de79b576337605d36d93fadfc3 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Wed, 22 Jun 2022 15:28:16 +0200 Subject: [PATCH] MINOR: freq_ctr: Add a function to get events excess over the current period freq_ctr_overshoot_period() function may be used to retrieve the excess of events over the current period for a givent frequency counter, ignoring the history. It is a way compare the "current rate" (the number of events over the current period) to a given rate and estimate the excess of events. It may be used to safely add new events, especially at the begining of the current period for a frequency counter with large period. This way, it is possible to smoothly add events during the whole period without quickly consuming all the quota at the beginning of the period and waiting for the next one to be able to add new events. --- include/haproxy/freq_ctr.h | 1 + src/freq_ctr.c | 58 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/include/haproxy/freq_ctr.h b/include/haproxy/freq_ctr.h index e0f50809dd..be9c9d894b 100644 --- a/include/haproxy/freq_ctr.h +++ b/include/haproxy/freq_ctr.h @@ -29,6 +29,7 @@ /* exported functions from freq_ctr.c */ ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend); +int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq); /* Update a frequency counter by incremental units. It is automatically * rotated if the period is over. It is important that it correctly initializes diff --git a/src/freq_ctr.c b/src/freq_ctr.c index 54aa78f02a..9d9ab3bfab 100644 --- a/src/freq_ctr.c +++ b/src/freq_ctr.c @@ -96,6 +96,64 @@ ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend) return past * remain + (curr + pend) * period; } +/* Returns the excess of events (may be negative) over the current period for + * target frequency . It returns 0 if the counter is in the future. The + * result considers the position of the current time within the current period. + * + * The caller may safely add new events if result is negative or null. + */ +int freq_ctr_overshoot_period(const struct freq_ctr *ctr, uint period, uint freq) +{ + uint curr, old_curr; + uint tick, old_tick; + int elapsed; + + tick = HA_ATOMIC_LOAD(&ctr->curr_tick); + curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); + + while (1) { + if (tick & 0x1) // change in progress + goto redo0; + + old_tick = tick; + old_curr = curr; + + /* now let's load the values a second time and make sure they + * did not change, which will indicate it was a stable reading. + */ + + tick = HA_ATOMIC_LOAD(&ctr->curr_tick); + if (tick & 0x1) // change in progress + goto redo0; + + if (tick != old_tick) + goto redo1; + + curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); + if (curr != old_curr) + goto redo2; + + /* all values match between two loads, they're stable, let's + * quit now. + */ + break; + redo0: + tick = HA_ATOMIC_LOAD(&ctr->curr_tick); + redo1: + curr = HA_ATOMIC_LOAD(&ctr->curr_ctr); + redo2: + __ha_cpu_relax(); + }; + + elapsed = HA_ATOMIC_LOAD(&global_now_ms) - tick; + if (unlikely(elapsed < 0)) { + /* The counter is in the future, there is no overshoot */ + return 0; + } + + return curr - div64_32((uint64_t)elapsed * freq, period); +} + /* * Local variables: * c-indent-level: 8