From: Willy Tarreau Date: Sun, 20 Jun 2010 05:15:43 +0000 (+0200) Subject: [MINOR] freq_ctr: add new types and functions for periods different from 1s X-Git-Tag: v1.5-dev8~541 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2970b0bedf81012089c111ccd8b04ca467120ec8;p=thirdparty%2Fhaproxy.git [MINOR] freq_ctr: add new types and functions for periods different from 1s Some freq counters will have to work on periods different from 1 second. The original freq counters rely on the period to be exactly one second. The new ones (freq_ctr_period) let the user define the period in ticks, and all computations are operated over that period. When reading a value, it indicates the amount of events over that period too. --- diff --git a/include/proto/freq_ctr.h b/include/proto/freq_ctr.h index 7add514807..d85e7210ff 100644 --- a/include/proto/freq_ctr.h +++ b/include/proto/freq_ctr.h @@ -56,6 +56,41 @@ static inline void update_freq_ctr(struct freq_ctr *ctr, unsigned int inc) /* Note: later we may want to propagate the update to other counters */ } +/* Rotate a frequency counter when current period is over. Must not be called + * during a valid period. It is important that it correctly initializes a null + * area. This one works on frequency counters which have a period different + * from one second. + */ +static inline void rotate_freq_ctr_period(struct freq_ctr_period *ctr, + unsigned int period) +{ + ctr->prev_ctr = ctr->curr_ctr; + ctr->curr_tick += period; + if (likely(now_ms - ctr->curr_tick >= period)) { + /* we missed at least two periods */ + ctr->prev_ctr = 0; + ctr->curr_tick = now_ms; + } + ctr->curr_ctr = 0; /* leave it at the end to help gcc optimize it away */ +} + +/* Update a frequency counter by incremental units. It is automatically + * rotated if the period is over. It is important that it correctly initializes + * a null area. This one works on frequency counters which have a period + * different from one second. + */ +static inline void update_freq_ctr_period(struct freq_ctr_period *ctr, + unsigned int period, unsigned int inc) +{ + if (likely(now_ms - ctr->curr_tick < period)) { + ctr->curr_ctr += inc; + return; + } + rotate_freq_ctr_period(ctr, period); + ctr->curr_ctr = inc; + /* Note: later we may want to propagate the update to other counters */ +} + /* Read a frequency counter taking history into account for missing time in * current period. */ @@ -75,6 +110,11 @@ unsigned int freq_ctr_remain(struct freq_ctr *ctr, unsigned int freq, unsigned i */ unsigned int next_event_delay(struct freq_ctr *ctr, unsigned int freq, unsigned int pend); +/* process freq counters over configurable periods */ +unsigned int read_freq_ctr_period(struct freq_ctr_period *ctr, unsigned int period); +unsigned int freq_ctr_remain_period(struct freq_ctr_period *ctr, unsigned int period, + unsigned int freq, unsigned int pend); + #endif /* _PROTO_FREQ_CTR_H */ /* diff --git a/include/types/freq_ctr.h b/include/types/freq_ctr.h index a8cfbd7d84..425ddf3b84 100644 --- a/include/types/freq_ctr.h +++ b/include/types/freq_ctr.h @@ -1,35 +1,50 @@ /* - include/types/freq_ctr.h - This file contains structure declarations for frequency counters. - - Copyright (C) 2000-2009 Willy Tarreau - w@1wt.eu - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation, version 2.1 - exclusively. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ + * include/types/freq_ctr.h + * This file contains structure declarations for frequency counters. + * + * Copyright (C) 2000-2010 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #ifndef _TYPES_FREQ_CTR_H #define _TYPES_FREQ_CTR_H #include +/* The implicit freq_ctr counter counts a rate of events per second. It is the + * preferred form to count rates over a one-second period, because it does not + * involve any divide. + */ struct freq_ctr { unsigned int curr_sec; /* start date of current period (seconds from now.tv_sec) */ unsigned int curr_ctr; /* cumulated value for current period */ unsigned int prev_ctr; /* value for last period */ }; +/* The generic freq_ctr_period counter counts a rate of events per period, where + * the period has to be known by the user. The period is measured in ticks and + * must be at least 2 ticks long. This form is slightly more CPU intensive than + * the per-second form. + */ +struct freq_ctr_period { + unsigned int curr_tick; /* start date of current period (wrapping ticks) */ + unsigned int curr_ctr; /* cumulated value for current period */ + unsigned int prev_ctr; /* value for last period */ +}; + #endif /* _TYPES_FREQ_CTR_H */ /* diff --git a/src/freq_ctr.c b/src/freq_ctr.c index 3df930fc4b..28f59369cf 100644 --- a/src/freq_ctr.c +++ b/src/freq_ctr.c @@ -1,7 +1,7 @@ /* * Event rate calculation functions. * - * Copyright 2000-2009 Willy Tarreau + * Copyright 2000-2010 Willy Tarreau * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -110,6 +110,82 @@ unsigned int next_event_delay(struct freq_ctr *ctr, unsigned int freq, unsigned return MAX(wait, 1); } +/* Reads a frequency counter taking history into account for missing time in + * current period. The period has to be passed in number of ticks and must + * match the one used to feed the counter. The counter value is reported for + * current date (now_ms). The return value has the same precision as one input + * data sample, so low rates over the period will be inaccurate but still + * appropriate for max checking. One trick we use for low values is to specially + * handle the case where the rate is between 0 and 1 in order to avoid flapping + * while waiting for the next event. + * + * For immediate limit checking, it's recommended to use freq_ctr_period_remain() + * instead which does not have the flapping correction, so that even frequencies + * as low as one event/period are properly handled. + * + * For measures over a 1-second period, it's better to use the implicit functions + * above. + */ +unsigned int read_freq_ctr_period(struct freq_ctr_period *ctr, unsigned int period) +{ + unsigned int curr, past; + unsigned int remain; + + curr = ctr->curr_ctr; + past = ctr->prev_ctr; + + remain = ctr->curr_tick + period - now_ms; + if (unlikely((int)remain < 0)) { + /* We're past the first period, check if we can still report a + * part of last period or if we're too far away. + */ + remain += period; + if ((int)remain < 0) + return 0; + past = curr; + curr = 0; + } + if (past <= 1 && !curr) + return past; /* very low rate, avoid flapping */ + + curr += div64_32((unsigned long long)past * remain, period); + return curr; +} + +/* Returns the number of remaining events that can occur on this freq counter + * while respecting events per period, and taking into account that + * events are already known to be pending. Returns 0 if limit was reached. + */ +unsigned int freq_ctr_remain_period(struct freq_ctr_period *ctr, unsigned int period, + unsigned int freq, unsigned int pend) +{ + unsigned int curr, past; + unsigned int remain; + + curr = ctr->curr_ctr; + past = ctr->prev_ctr; + + remain = ctr->curr_tick + period - now_ms; + if (likely((int)remain < 0)) { + /* We're past the first period, check if we can still report a + * part of last period or if we're too far away. + */ + past = curr; + curr = 0; + remain += period; + if ((int)remain < 0) + past = 0; + } + if (likely(past)) + curr += div64_32((unsigned long long)past * remain, period); + + curr += pend; + freq -= curr; + if ((int)freq < 0) + freq = 0; + return freq; +} + /* * Local variables: