counter(9): rate limit periods may be more than 1 second
Teach counter_rate() to deal with periods of more than 1 second, so we can
express 'at most 100 in 10 seconds', which is different from 'at most 10 in
1 second'.
While here move the struct counter_rate definition into subr_counter.c so users
cannot mess with its internals. Add allocation and free functions.
Reviewed by: glebius
Sponsored by: Rubicon Communications, LLC ("Netgate")
Differential Revision: https://reviews.freebsd.org/D50796
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd March 11, 2021
|
||||
.Dd June 19, 2025
|
||||
.Dt COUNTER 9
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -49,8 +49,14 @@
|
||||
.Fn counter_u64_fetch "counter_u64_t c"
|
||||
.Ft void
|
||||
.Fn counter_u64_zero "counter_u64_t c"
|
||||
.Ft struct counter_rate *
|
||||
.Fn counter_rate_alloc "int flags" "int period"
|
||||
.Ft int64_t
|
||||
.Fn counter_ratecheck "struct counter_rate *cr" "int64_t limit"
|
||||
.Ft uint64_t
|
||||
.Fn counter_rate_get "struct counter_rate *cr"
|
||||
.Ft void
|
||||
.Fn counter_rate_free "struct counter_rate *cr"
|
||||
.Fn COUNTER_U64_SYSINIT "counter_u64_t c"
|
||||
.Fn COUNTER_U64_DEFINE_EARLY "counter_u64_t c"
|
||||
.In sys/sysctl.h
|
||||
@@ -133,6 +139,13 @@ value for any moment.
|
||||
Clear the counter
|
||||
.Fa c
|
||||
and set it to zero.
|
||||
.It Fn counter_rate_alloc flags period
|
||||
Allocate a new struct counter_rate.
|
||||
.Fa flags
|
||||
is passed to
|
||||
.Xr malloc 9 .
|
||||
.Fa period
|
||||
is the time over which the rate is checked.
|
||||
.It Fn counter_ratecheck cr limit
|
||||
The function is a multiprocessor-friendly version of
|
||||
.Fn ppsratecheck
|
||||
@@ -140,11 +153,17 @@ which uses
|
||||
.Nm
|
||||
internally.
|
||||
Returns non-negative value if the rate is not yet reached during the current
|
||||
second, and a negative value otherwise.
|
||||
If the limit was reached on previous second, but was just reset back to zero,
|
||||
then
|
||||
period, and a negative value otherwise.
|
||||
If the limit was reached during the previous period, but was just reset back
|
||||
to zero, then
|
||||
.Fn counter_ratecheck
|
||||
returns number of events since previous reset.
|
||||
.It Fn counter_rate_get cr
|
||||
The number of hits to this check within the current period.
|
||||
.It Fn counter_rate_free cr
|
||||
Free the
|
||||
.Fa cr
|
||||
counter.
|
||||
.It Fn COUNTER_U64_SYSINIT c
|
||||
Define a
|
||||
.Xr SYSINIT 9
|
||||
|
||||
+55
-2
@@ -40,6 +40,8 @@
|
||||
#define IN_SUBR_COUNTER_C
|
||||
#include <sys/counter.h>
|
||||
|
||||
static MALLOC_DEFINE(M_COUNTER_RATE, "counter_rate", "counter rate allocations");
|
||||
|
||||
void
|
||||
counter_u64_zero(counter_u64_t c)
|
||||
{
|
||||
@@ -114,6 +116,57 @@ sysctl_handle_counter_u64_array(SYSCTL_HANDLER_ARGS)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* counter(9) based rate checking.
|
||||
*/
|
||||
struct counter_rate {
|
||||
counter_u64_t cr_rate; /* Events since last second */
|
||||
volatile int cr_lock; /* Lock to clean the struct */
|
||||
int cr_ticks; /* Ticks on last clean */
|
||||
int cr_over; /* Over limit since cr_ticks? */
|
||||
int cr_period; /* Allow cr_rate per cr_period seconds. */
|
||||
};
|
||||
|
||||
struct counter_rate *
|
||||
counter_rate_alloc(int flags, int period)
|
||||
{
|
||||
struct counter_rate *new;
|
||||
|
||||
new = malloc(sizeof(struct counter_rate), M_COUNTER_RATE,
|
||||
flags | M_ZERO);
|
||||
if (new == NULL)
|
||||
return (NULL);
|
||||
|
||||
new->cr_rate = counter_u64_alloc(flags);
|
||||
if (new->cr_rate == NULL) {
|
||||
free(new, M_COUNTER_RATE);
|
||||
return (NULL);
|
||||
}
|
||||
new->cr_ticks = ticks;
|
||||
new->cr_period = period;
|
||||
|
||||
return (new);
|
||||
}
|
||||
|
||||
void
|
||||
counter_rate_free(struct counter_rate *rate)
|
||||
{
|
||||
if (rate == NULL)
|
||||
return;
|
||||
|
||||
counter_u64_free(rate->cr_rate);
|
||||
free(rate, M_COUNTER_RATE);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
counter_rate_get(struct counter_rate *cr)
|
||||
{
|
||||
if (cr->cr_ticks < (tick - (hz * cr->cr_period)))
|
||||
return (0);
|
||||
|
||||
return (counter_u64_fetch(cr->cr_rate));
|
||||
}
|
||||
|
||||
/*
|
||||
* MP-friendly version of ppsratecheck().
|
||||
*
|
||||
@@ -132,7 +185,7 @@ counter_ratecheck(struct counter_rate *cr, int64_t limit)
|
||||
val = cr->cr_over;
|
||||
now = ticks;
|
||||
|
||||
if ((u_int)(now - cr->cr_ticks) >= hz) {
|
||||
if ((u_int)(now - cr->cr_ticks) >= (hz * cr->cr_period)) {
|
||||
/*
|
||||
* Time to clear the structure, we are in the next second.
|
||||
* First try unlocked read, and then proceed with atomic.
|
||||
@@ -143,7 +196,7 @@ counter_ratecheck(struct counter_rate *cr, int64_t limit)
|
||||
* Check if other thread has just went through the
|
||||
* reset sequence before us.
|
||||
*/
|
||||
if ((u_int)(now - cr->cr_ticks) >= hz) {
|
||||
if ((u_int)(now - cr->cr_ticks) >= (hz * cr->cr_period)) {
|
||||
val = counter_u64_fetch(cr->cr_rate);
|
||||
counter_u64_zero(cr->cr_rate);
|
||||
cr->cr_over = 0;
|
||||
|
||||
@@ -1090,7 +1090,7 @@ ip_next_mtu(int mtu, int dir)
|
||||
* the 'final' error, but it doesn't make sense to solve the printing
|
||||
* delay with more complex code.
|
||||
*/
|
||||
VNET_DEFINE_STATIC(struct counter_rate, icmp_rates[BANDLIM_MAX]);
|
||||
VNET_DEFINE_STATIC(struct counter_rate *, icmp_rates[BANDLIM_MAX]);
|
||||
#define V_icmp_rates VNET(icmp_rates)
|
||||
|
||||
static const char *icmp_rate_descrs[BANDLIM_MAX] = {
|
||||
@@ -1158,8 +1158,7 @@ icmp_bandlimit_init(void)
|
||||
{
|
||||
|
||||
for (int i = 0; i < BANDLIM_MAX; i++) {
|
||||
V_icmp_rates[i].cr_rate = counter_u64_alloc(M_WAITOK);
|
||||
V_icmp_rates[i].cr_ticks = ticks;
|
||||
V_icmp_rates[i] = counter_rate_alloc(M_WAITOK, 1);
|
||||
icmplim_new_jitter(i);
|
||||
}
|
||||
}
|
||||
@@ -1172,7 +1171,7 @@ icmp_bandlimit_uninit(void)
|
||||
{
|
||||
|
||||
for (int i = 0; i < BANDLIM_MAX; i++)
|
||||
counter_u64_free(V_icmp_rates[i].cr_rate);
|
||||
counter_rate_free(V_icmp_rates[i]);
|
||||
}
|
||||
VNET_SYSUNINIT(icmp_bandlimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD,
|
||||
icmp_bandlimit_uninit, NULL);
|
||||
@@ -1189,7 +1188,7 @@ badport_bandlim(int which)
|
||||
KASSERT(which >= 0 && which < BANDLIM_MAX,
|
||||
("%s: which %d", __func__, which));
|
||||
|
||||
pps = counter_ratecheck(&V_icmp_rates[which], V_icmplim +
|
||||
pps = counter_ratecheck(V_icmp_rates[which], V_icmplim +
|
||||
V_icmplim_curr_jitter[which]);
|
||||
if (pps > 0) {
|
||||
if (V_icmplim_output)
|
||||
|
||||
@@ -2844,7 +2844,7 @@ sysctl_icmp6lim_and_jitter(SYSCTL_HANDLER_ARGS)
|
||||
}
|
||||
|
||||
|
||||
VNET_DEFINE_STATIC(struct counter_rate, icmp6_rates[RATELIM_MAX]);
|
||||
VNET_DEFINE_STATIC(struct counter_rate *, icmp6_rates[RATELIM_MAX]);
|
||||
#define V_icmp6_rates VNET(icmp6_rates)
|
||||
|
||||
static void
|
||||
@@ -2852,8 +2852,7 @@ icmp6_ratelimit_init(void)
|
||||
{
|
||||
|
||||
for (int i = 0; i < RATELIM_MAX; i++) {
|
||||
V_icmp6_rates[i].cr_rate = counter_u64_alloc(M_WAITOK);
|
||||
V_icmp6_rates[i].cr_ticks = ticks;
|
||||
V_icmp6_rates[i] = counter_rate_alloc(M_WAITOK, 1);
|
||||
icmp6lim_new_jitter(i);
|
||||
}
|
||||
}
|
||||
@@ -2866,7 +2865,7 @@ icmp6_ratelimit_uninit(void)
|
||||
{
|
||||
|
||||
for (int i = 0; i < RATELIM_MAX; i++)
|
||||
counter_u64_free(V_icmp6_rates[i].cr_rate);
|
||||
counter_rate_free(V_icmp6_rates[i]);
|
||||
}
|
||||
VNET_SYSUNINIT(icmp6_ratelimit, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD,
|
||||
icmp6_ratelimit_uninit, NULL);
|
||||
@@ -2916,7 +2915,7 @@ icmp6_ratelimit(const struct in6_addr *dst, const int type, const int code)
|
||||
break;
|
||||
};
|
||||
|
||||
pps = counter_ratecheck(&V_icmp6_rates[which], V_icmp6errppslim +
|
||||
pps = counter_ratecheck(V_icmp6_rates[which], V_icmp6errppslim +
|
||||
V_icmp6lim_curr_jitter[which]);
|
||||
if (pps > 0) {
|
||||
if (V_icmp6lim_output)
|
||||
|
||||
+4
-9
@@ -60,17 +60,12 @@ uint64_t counter_u64_fetch(counter_u64_t);
|
||||
counter_u64_zero((a)[_i]); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* counter(9) based rate checking.
|
||||
*/
|
||||
struct counter_rate {
|
||||
counter_u64_t cr_rate; /* Events since last second */
|
||||
volatile int cr_lock; /* Lock to clean the struct */
|
||||
int cr_ticks; /* Ticks on last clean */
|
||||
int cr_over; /* Over limit since cr_ticks? */
|
||||
};
|
||||
struct counter_rate;
|
||||
|
||||
struct counter_rate *counter_rate_alloc(int flags, int period);
|
||||
void counter_rate_free(struct counter_rate *);
|
||||
int64_t counter_ratecheck(struct counter_rate *, int64_t);
|
||||
uint64_t counter_rate_get(struct counter_rate *);
|
||||
|
||||
#define COUNTER_U64_SYSINIT(c) \
|
||||
SYSINIT(c##_counter_sysinit, SI_SUB_COUNTER, \
|
||||
|
||||
Reference in New Issue
Block a user