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.
This commit is contained in:
Christopher Faulet 2022-06-22 15:28:16 +02:00
parent dbbdb25f1c
commit aa55640b8c
2 changed files with 59 additions and 0 deletions

View File

@ -29,6 +29,7 @@
/* exported functions from freq_ctr.c */ /* exported functions from freq_ctr.c */
ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend); 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 <inc> incremental units. It is automatically /* Update a frequency counter by <inc> incremental units. It is automatically
* rotated if the period is over. It is important that it correctly initializes * rotated if the period is over. It is important that it correctly initializes

View File

@ -96,6 +96,64 @@ ullong freq_ctr_total(const struct freq_ctr *ctr, uint period, int pend)
return past * remain + (curr + pend) * period; return past * remain + (curr + pend) * period;
} }
/* Returns the excess of events (may be negative) over the current period for
* target frequency <freq>. 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: * Local variables:
* c-indent-level: 8 * c-indent-level: 8