From nobody Wed Jun 25 17:56:37 2025 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4bS8df3f8pz60Kbm; Wed, 25 Jun 2025 17:56:38 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R10" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4bS8df0tDkz3v0m; Wed, 25 Jun 2025 17:56:38 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1750874198; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=bitcenbwQ7fA5LkBRoiD8NMRJ3b9KvdZj5L9OTNTLzQ=; b=MAVA9ejV7nbnx/pPFt+GfrghS3xHVBDVkL69JSXEb8FxKLpzwUDoIUsuGK7iYQ2Za/Bj/m NjJCQd2Jnh4SBY4ByYG3Y84+l5G3QutOo9lgU8+RxcdLFEpqsJxLv7sK5wGvZ89SF9w058 U52Ul53iJnWaE1/FNg1jpqun2cRrYbLoCkLABJh4n9Kfpgj766lOpGB4HfEtnW00AfRpNz +HE9wXP7FeA/w3QqbmFn4R02ydqj0SEY1CJ2jhYcPKiOj3XbuLPk1tIYP8f1wbj8xZcLjQ Y0dGoXVpM9sTDGJldZh1HdwUmzyXBm11X/d7nGhs6PdP/GrgdPhxo0LBCV14vA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1750874198; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=bitcenbwQ7fA5LkBRoiD8NMRJ3b9KvdZj5L9OTNTLzQ=; b=xazIuRpjtez8xUCG2k8eurd9+zlhqYMwhjc/6DnPNXL7ZHKZlAL+1AStVkqp7OfaNqJROW ehzmCYOof2qomFJViKPnqaSKonHhAzNtzmigLkx5kveVs7J29nQQz+93VPp9byn+lCMxto TIjUDYFG23DaF2LtbFFnaHLl20GHWw5qG5qzZMFuTs5pxKYk4CG57M77zb5vCTtERpx64a b3F++pkYfXHUA33gRwXIXVHYS7neIJ/AEZejIPiT10A3tW8HaU8t3usyPy7kv1/lzbGO9t MFEsWsDg53QJjhij4BN/HdTU9Y4jm17/LCfp3tFIjBUcds9cL5BrcZ8qpWBSPQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1750874198; a=rsa-sha256; cv=none; b=Afhdfq/8smR0834vpmHrVjAt2IXEC+IFRCk2JzF1/v2qUwAB4PuMXK2S9Ak+G14yfIozXS kql6nXXwJ4WdCEzL9BtBXtFF/G8lR9DuSz658+9XG6pP4j8kfF77wwYDdSg0V5DbMk9k2N w8r/tvxqexaRyVH3L/ACxEk0WVYFjllOPF4YT0I6PfI9CEn1BijFDAcya/aZRetr8R18i+ ZoSaH+uqqhlFmqMtw2mm35ma0gO2zgE3/Y2KLWOzKkXm88gxlFgl0KFFmRvY4OCN61V7ec CepoOn09F09ZnL/NqtKErJClTPVf7qwd7JQHVkctTIZHuia2VaSzQbP271ht8g== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4bS8df0H6lzC2R; Wed, 25 Jun 2025 17:56:38 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 55PHubXb035287; Wed, 25 Jun 2025 17:56:37 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 55PHubhT035284; Wed, 25 Jun 2025 17:56:37 GMT (envelope-from git) Date: Wed, 25 Jun 2025 17:56:37 GMT Message-Id: <202506251756.55PHubhT035284@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: 1cd5c35d136e - main - counter(9): rate limit periods may be more than 1 second List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 1cd5c35d136e2c8605b9ba23e6a5879539411947 Auto-Submitted: auto-generated The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=1cd5c35d136e2c8605b9ba23e6a5879539411947 commit 1cd5c35d136e2c8605b9ba23e6a5879539411947 Author: Kristof Provost AuthorDate: 2025-06-03 07:07:14 +0000 Commit: Kristof Provost CommitDate: 2025-06-25 17:56:22 +0000 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 --- share/man/man9/counter.9 | 27 +++++++++++++++++++---- sys/kern/subr_counter.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++-- sys/netinet/ip_icmp.c | 9 ++++---- sys/netinet6/icmp6.c | 9 ++++---- sys/sys/counter.h | 13 ++++------- 5 files changed, 90 insertions(+), 25 deletions(-) diff --git a/share/man/man9/counter.9 b/share/man/man9/counter.9 index 1d3f3281ac0b..05af87e8263e 100644 --- a/share/man/man9/counter.9 +++ b/share/man/man9/counter.9 @@ -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 diff --git a/sys/kern/subr_counter.c b/sys/kern/subr_counter.c index 2cb987cb67f9..b629abc99315 100644 --- a/sys/kern/subr_counter.c +++ b/sys/kern/subr_counter.c @@ -40,6 +40,8 @@ #define IN_SUBR_COUNTER_C #include +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; diff --git a/sys/netinet/ip_icmp.c b/sys/netinet/ip_icmp.c index 17d15d7d9629..cb4b6df57c57 100644 --- a/sys/netinet/ip_icmp.c +++ b/sys/netinet/ip_icmp.c @@ -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) diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 9ea640fd24c8..d89515d7eda5 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -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) diff --git a/sys/sys/counter.h b/sys/sys/counter.h index 65d6a54b47a6..e303fc4ecbc2 100644 --- a/sys/sys/counter.h +++ b/sys/sys/counter.h @@ -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, \