* [PATCH net-next v2 1/4] netconsole: add a per-target message rate limit
2026-09-10 13:46 [PATCH net-next v2 0/4] netconsole: Support messages ratelimit-ing Breno Leitao
@ 2026-09-10 13:46 ` Breno Leitao
2026-09-12 0:06 ` netdev-bot+sashiko
2026-09-10 13:46 ` [PATCH net-next v2 2/4] netconsole: allow configuring the rate limit interval through configfs Breno Leitao
` (2 subsequent siblings)
3 siblings, 1 reply; 9+ messages in thread
From: Breno Leitao @ 2026-09-10 13:46 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: Randy Dunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, Breno Leitao,
kernel-team
Give each target a token bucket and consult it once per message in
netconsole_write().
The bucket is created with a zero interval, which struct ratelimit_state
treats as unlimited, and nothing can set a nonzero one yet, so no target
changes behaviour.
Skip the bucket while oops_in_progress is set, so a limit configured for
steady-state logging never truncates an oops, BUG() or panic().
The configfs files that expose it come next.
___ratelimit() only trylocks its own raw spinlock, so it is safe with
target_list_lock held and interrupts disabled, and safe from NMI. Set
RATELIMIT_MSG_ON_RELEASE so it does not report the suppressed count
itself, which would printk() from inside the console being serviced.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/net/netconsole.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index b358e5c3673510..13292c3f818e9f 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -49,6 +49,7 @@
#include <linux/rtnetlink.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
+#include <linux/ratelimit.h>
MODULE_AUTHOR("Matt Mackall <mpm@selenic.com>");
MODULE_DESCRIPTION("Console driver for network interfaces");
@@ -175,6 +176,7 @@ struct netcons_userdata {
* @sysdata: Cached, formatted string of append
* @sysdata_fields: Sysdata features enabled.
* @msgcounter: Message sent counter.
+ * @ratelimit: Token bucket used to rate limit messages
* @stats: Packet send stats for the target. Used for debugging.
* @state: State of the target.
* Visible from userspace (read-write).
@@ -219,6 +221,7 @@ struct netconsole_target {
u32 sysdata_fields;
/* protected by target_list_lock */
u32 msgcounter;
+ struct ratelimit_state ratelimit;
#endif
struct netconsole_target_stats stats;
enum target_state state;
@@ -282,6 +285,26 @@ static void dynamic_netconsole_mutex_unlock(void)
mutex_unlock(&dynamic_netconsole_mutex);
}
+static void netconsole_ratelimit_init(struct netconsole_target *nt)
+{
+ ratelimit_state_init(&nt->ratelimit, 0, DEFAULT_RATELIMIT_BURST);
+ /* The flag keeps ___ratelimit() from reporting the suppressed count
+ * itself, which would printk() from inside the console being
+ * serviced. Nothing calls ratelimit_state_exit(), so the count is
+ * never reported on release either.
+ */
+ ratelimit_set_flags(&nt->ratelimit, RATELIMIT_MSG_ON_RELEASE);
+}
+
+static bool netconsole_ratelimited(struct netconsole_target *nt)
+{
+ /* A limit meant for steady-state logging must not eat a crash dump. */
+ if (oops_in_progress)
+ return false;
+
+ return !__ratelimit(&nt->ratelimit);
+}
+
#else /* !CONFIG_NETCONSOLE_DYNAMIC */
static int __init dynamic_netconsole_init(void)
@@ -318,6 +341,15 @@ static void dynamic_netconsole_mutex_unlock(void)
{
}
+static void netconsole_ratelimit_init(struct netconsole_target *nt)
+{
+}
+
+static bool netconsole_ratelimited(struct netconsole_target *nt)
+{
+ return false;
+}
+
#endif /* CONFIG_NETCONSOLE_DYNAMIC */
/* Check if the target was bound by mac address. */
@@ -686,6 +718,7 @@ static struct netconsole_target *alloc_and_init(void)
nt->remote_port = 6666;
eth_broadcast_addr(nt->remote_mac);
nt->state = STATE_DISABLED;
+ netconsole_ratelimit_init(nt);
INIT_WORK(&nt->resume_wq, process_resume_target);
/* Set up the skb pool primitives once; enabling only refills it. */
skb_queue_head_init(&nt->skb_pool);
@@ -2482,6 +2515,9 @@ static void netconsole_write(struct nbcon_write_context *wctxt, bool extended)
!netif_running(nt->np.dev))
continue;
+ if (netconsole_ratelimited(nt))
+ continue;
+
/* If nbcon_enter_unsafe() fails, just return given netconsole
* lost the ownership, and iterating over the targets will not
* be able to re-acquire.
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH net-next v2 1/4] netconsole: add a per-target message rate limit
2026-09-10 13:46 ` [PATCH net-next v2 1/4] netconsole: add a per-target message rate limit Breno Leitao
@ 2026-09-12 0:06 ` netdev-bot+sashiko
0 siblings, 0 replies; 9+ messages in thread
From: netdev-bot+sashiko @ 2026-09-12 0:06 UTC (permalink / raw)
To: leitao
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, corbet,
skhan, shuah, rdunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, kernel-team
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 0 · Low: 1
- [High] The oops_in_progress bypass in netconsole_ratelimited()
(drivers/net/netconsole.c:299-306) does not deliver the guarantee its…
- [Low] In netconsole_write() (drivers/net/netconsole.c:2518-2534) the
rate-limit token is consumed by netconsole_ratelimited() ->…
--
netconsole: add a per-target message rate limit
This adds a struct ratelimit_state token bucket to each netconsole target
and consults it once per message in netconsole_write(). The bucket is
initialised with a zero interval (unlimited) and the check is skipped while
oops_in_progress is set.
> diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
> index b358e5c367351..13292c3f818e9 100644
> --- a/drivers/net/netconsole.c
> +++ b/drivers/net/netconsole.c
[ ... ]
> @@ -282,6 +285,26 @@ static void dynamic_netconsole_mutex_unlock(void)
> mutex_unlock(&dynamic_netconsole_mutex);
> }
>
> +static void netconsole_ratelimit_init(struct netconsole_target *nt)
> +{
> + ratelimit_state_init(&nt->ratelimit, 0, DEFAULT_RATELIMIT_BURST);
> + /* The flag keeps ___ratelimit() from reporting the suppressed count
> + * itself, which would printk() from inside the console being
> + * serviced. Nothing calls ratelimit_state_exit(), so the count is
> + * never reported on release either.
> + */
> + ratelimit_set_flags(&nt->ratelimit, RATELIMIT_MSG_ON_RELEASE);
> +}
> +
> +static bool netconsole_ratelimited(struct netconsole_target *nt)
> +{
> + /* A limit meant for steady-state logging must not eat a crash dump. */
> + if (oops_in_progress)
> + return false;
> +
> + return !__ratelimit(&nt->ratelimit);
> +}
[Severity: High]
Does the oops_in_progress test in netconsole_ratelimited() actually cover
oops and BUG() output on netconsole? The changelog says:
Skip the bucket while oops_in_progress is set, so a limit configured
for steady-state logging never truncates an oops, BUG() or panic().
and the documentation patch later in the series repeats it ("Crash output
bypasses the bucket. While an oops, BUG() or panic() is in progress every
message is sent").
Both consoles are registered with CON_NBCON_ATOMIC_UNSAFE:
drivers/net/netconsole.c
static struct console netconsole = {
.name = "netcon",
.flags = CON_ENABLED | CON_NBCON | CON_NBCON_ATOMIC_UNSAFE,
and console_is_usable() refuses such a write_atomic() callback unless
unsafe takeovers are allowed:
include/linux/console.h:console_is_usable() {
...
if ((flags & CON_NBCON_ATOMIC_UNSAFE) && !nbcon_allow_unsafe_takeover())
return false;
...
}
kernel/printk/nbcon.c
bool nbcon_allow_unsafe_takeover(void)
{
return panic_on_this_cpu() && panic_nbcon_allow_unsafe_takeover;
}
So during a non-panic oops the atomic flush skips netconsole entirely. In
the emergency context that oops_enter() establishes, only nbcon_atomic is
selected, not nbcon_offload:
kernel/printk/internal.h:printk_get_console_flush_type() {
...
case NBCON_PRIO_EMERGENCY:
if (have_nbcon_console && !have_boot_console)
ft->nbcon_atomic = true;
...
}
which means the netconsole kthread is not woken while the backtrace is
being emitted. The wake happens later, from
nbcon_cpu_emergency_exit() -> nbcon_kthreads_wake(), reached via
oops_exit(), and on x86 oops_end() runs bust_spinlocks(0) (which drops
oops_in_progress back to 0) before oops_exit().
Doesn't that mean every record of an oops or BUG() backtrace reaches
netconsole_write() with oops_in_progress == 0 and is charged against the
bucket? With the configfs interval knob from the following patch set to a
nonzero value, a backtrace is many more records than
DEFAULT_RATELIMIT_BURST (10), so would it be silently truncated? panic()
looks protected because it does bust_spinlocks(1) before the panic-CPU
unsafe atomic flush, but oops/BUG do not appear to be.
Would keying the bypass off the nbcon record/context priority, or the nbcon
emergency nesting count, work better here than the global oops_in_progress
flag?
[ ... ]
> @@ -2482,6 +2515,9 @@ static void netconsole_write(struct nbcon_write_context *wctxt, bool extended)
> !netif_running(nt->np.dev))
> continue;
>
> + if (netconsole_ratelimited(nt))
> + continue;
> +
> /* If nbcon_enter_unsafe() fails, just return given netconsole
> * lost the ownership, and iterating over the targets will not
> * be able to re-acquire.
[Severity: Low]
This isn't much of a bug, but is the token spent a bit early here?
netconsole_ratelimited() -> __ratelimit() decrements rs->rs_n_left:
lib/ratelimit.c:___ratelimit() {
...
if (atomic_read(&rs->rs_n_left) > 0 && atomic_dec_return(&rs->rs_n_left) >= 0)
ret = 1;
...
}
and only afterwards does netconsole_write() check ownership:
drivers/net/netconsole.c:netconsole_write() {
...
if (!nbcon_enter_unsafe(wctxt))
return;
...
}
If nbcon_enter_unsafe() returns false the message is never transmitted, yet
the token is gone and there is no way to give it back. The return value of
nbcon_exit_unsafe() is also ignored, so a further loop iteration can spend
another target's token before lost ownership is noticed.
In practice this only shows up on the panic-CPU unsafe takeover path, since
console_is_usable() rejects netconsole's write_atomic() for every other
context, and there the re-emitted record hits the oops_in_progress bypass
anyway. Would consuming the token only after the write context is acquired
(or only on a successful send) be cleaner?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260910-netcons_ratelimit-v2-0-ebf0dd91e26e%40debian.org
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH net-next v2 2/4] netconsole: allow configuring the rate limit interval through configfs
2026-09-10 13:46 [PATCH net-next v2 0/4] netconsole: Support messages ratelimit-ing Breno Leitao
2026-09-10 13:46 ` [PATCH net-next v2 1/4] netconsole: add a per-target message rate limit Breno Leitao
@ 2026-09-10 13:46 ` Breno Leitao
2026-09-12 0:06 ` netdev-bot+sashiko
2026-09-10 13:46 ` [PATCH net-next v2 3/4] netconsole: allow configuring the rate limit burst " Breno Leitao
2026-09-10 13:46 ` [PATCH net-next v2 4/4] docs: netconsole: document rate limit feature Breno Leitao
3 siblings, 1 reply; 9+ messages in thread
From: Breno Leitao @ 2026-09-10 13:46 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: Randy Dunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, Breno Leitao,
kernel-team
The per-target token bucket has no interface, so every target is still
unlimited.
Expose the interval as ratelimit_interval_ms through configfs. It
defaults to zero, so a target stays unlimited until an administrator
sets one.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/net/netconsole.c | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index 13292c3f818e9f..faf8f9bbc6c0c2 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -945,6 +945,14 @@ static ssize_t transmit_errors_show(struct config_item *item, char *buf)
return sysfs_emit(buf, "%llu\n", xmit_drop_count + enomem_count);
}
+static ssize_t ratelimit_interval_ms_show(struct config_item *item, char *buf)
+{
+ struct netconsole_target *nt = to_target(item);
+
+ return sysfs_emit(buf, "%u\n",
+ jiffies_to_msecs(READ_ONCE(nt->ratelimit.interval)));
+}
+
/* configfs helper to display if cpu_nr sysdata feature is enabled */
static ssize_t sysdata_cpu_nr_enabled_show(struct config_item *item, char *buf)
{
@@ -1340,6 +1348,29 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf,
return ret;
}
+static ssize_t ratelimit_interval_ms_store(struct config_item *item,
+ const char *buf, size_t count)
+{
+ struct netconsole_target *nt = to_target(item);
+ unsigned int interval;
+ unsigned long jifs;
+ ssize_t ret;
+
+ ret = kstrtouint(buf, 10, &interval);
+ if (ret)
+ return ret;
+
+ jifs = msecs_to_jiffies(interval);
+ if (jifs > INT_MAX)
+ return -ERANGE;
+
+ dynamic_netconsole_mutex_lock();
+ WRITE_ONCE(nt->ratelimit.interval, jifs);
+ dynamic_netconsole_mutex_unlock();
+
+ return count;
+}
+
struct userdatum {
struct config_item item;
char value[MAX_EXTRADATA_VALUE_LEN];
@@ -1704,6 +1735,7 @@ CONFIGFS_ATTR_RO(, local_mac);
CONFIGFS_ATTR(, remote_mac);
CONFIGFS_ATTR(, release);
CONFIGFS_ATTR_RO(, transmit_errors);
+CONFIGFS_ATTR(, ratelimit_interval_ms);
static struct configfs_attribute *netconsole_target_attrs[] = {
&attr_enabled,
@@ -1717,6 +1749,7 @@ static struct configfs_attribute *netconsole_target_attrs[] = {
&attr_local_mac,
&attr_remote_mac,
&attr_transmit_errors,
+ &attr_ratelimit_interval_ms,
NULL,
};
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH net-next v2 2/4] netconsole: allow configuring the rate limit interval through configfs
2026-09-10 13:46 ` [PATCH net-next v2 2/4] netconsole: allow configuring the rate limit interval through configfs Breno Leitao
@ 2026-09-12 0:06 ` netdev-bot+sashiko
0 siblings, 0 replies; 9+ messages in thread
From: netdev-bot+sashiko @ 2026-09-12 0:06 UTC (permalink / raw)
To: leitao
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, corbet,
skhan, shuah, rdunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, kernel-team
Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 0 · Low: 3
- [Low] DISCARDED (resolved later in the series).
- [Low] DISCARDED (documented as an intentional exception later in the
series).
- [Low] Wrong range bound in ratelimit_interval_ms_store()
(drivers/net/netconsole.c:1363-1365): the user value is converted…
--
netconsole: allow configuring the rate limit interval through configfs
This exposes the per-target token bucket interval as a new configfs
attribute, ratelimit_interval_ms. The value is converted from
milliseconds to jiffies on store and back to milliseconds on show, and
defaults to zero so a target remains unlimited unless configured.
> diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
> index 13292c3f818e9..faf8f9bbc6c0c 100644
> --- a/drivers/net/netconsole.c
> +++ b/drivers/net/netconsole.c
> @@ -945,6 +945,14 @@ static ssize_t transmit_errors_show(struct config_item *item, char *buf)
> return sysfs_emit(buf, "%llu\n", xmit_drop_count + enomem_count);
> }
>
> +static ssize_t ratelimit_interval_ms_show(struct config_item *item, char *buf)
> +{
> + struct netconsole_target *nt = to_target(item);
> +
> + return sysfs_emit(buf, "%u\n",
> + jiffies_to_msecs(READ_ONCE(nt->ratelimit.interval)));
> +}
> +
[ ... ]
> @@ -1340,6 +1348,29 @@ static ssize_t remote_mac_store(struct config_item *item, const char *buf,
> return ret;
> }
>
> +static ssize_t ratelimit_interval_ms_store(struct config_item *item,
> + const char *buf, size_t count)
> +{
> + struct netconsole_target *nt = to_target(item);
> + unsigned int interval;
> + unsigned long jifs;
> + ssize_t ret;
> +
> + ret = kstrtouint(buf, 10, &interval);
> + if (ret)
> + return ret;
> +
> + jifs = msecs_to_jiffies(interval);
> + if (jifs > INT_MAX)
> + return -ERANGE;
[Severity: Low]
Can the "jifs > INT_MAX" test in ratelimit_interval_ms_store() ever fire on
a 32-bit kernel?
msecs_to_jiffies() saturates before the comparison happens:
kernel/time/time.c:__msecs_to_jiffies() {
if ((int)m < 0)
return MAX_JIFFY_OFFSET;
return _msecs_to_jiffies(m);
}
and MAX_JIFFY_OFFSET is defined as:
include/linux/jiffies.h:
#define MAX_JIFFY_OFFSET ((LONG_MAX >> 1)-1)
On 32-bit that is 1073741822, which is smaller than INT_MAX (2147483647).
For every supported HZ <= 1000 the non-saturating path returns at most
"interval" itself, so on 32-bit the check looks like dead code.
The visible effect is that the same write behaves differently per
architecture. With CONFIG_NETCONSOLE_DYNAMIC on 32-bit:
echo 3000000000 > /sys/kernel/config/netconsole/<t>/ratelimit_interval_ms
kstrtouint() accepts the value, __msecs_to_jiffies() takes the "(int)m < 0"
branch and returns MAX_JIFFY_OFFSET, the range check passes, and
WRITE_ONCE() stores 1073741822 into nt->ratelimit.interval. The write
returns success, while the identical write on a 64-bit kernel returns
-ERANGE.
Would checking MAX_JIFFY_OFFSET, or validating "interval" before the
conversion, express the intent better here?
> +
> + dynamic_netconsole_mutex_lock();
> + WRITE_ONCE(nt->ratelimit.interval, jifs);
> + dynamic_netconsole_mutex_unlock();
> +
> + return count;
> +}
> +
[Severity: Low]
Following on from the clamping above, can the read-back in
ratelimit_interval_ms_show() wrap on 32-bit?
include/linux/jiffies.h:
static inline unsigned int jiffies_to_msecs(const unsigned long j)
{
return (MSEC_PER_SEC / HZ) * j;
}
That multiplication is done in 32-bit arithmetic. With HZ=100 and the
stored value 1073741822 from the example above, the attribute prints
2147483628 rather than 10737418220, so the value read back is unrelated to
what was written.
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260910-netcons_ratelimit-v2-0-ebf0dd91e26e%40debian.org
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH net-next v2 3/4] netconsole: allow configuring the rate limit burst through configfs
2026-09-10 13:46 [PATCH net-next v2 0/4] netconsole: Support messages ratelimit-ing Breno Leitao
2026-09-10 13:46 ` [PATCH net-next v2 1/4] netconsole: add a per-target message rate limit Breno Leitao
2026-09-10 13:46 ` [PATCH net-next v2 2/4] netconsole: allow configuring the rate limit interval through configfs Breno Leitao
@ 2026-09-10 13:46 ` Breno Leitao
2026-09-12 0:06 ` netdev-bot+sashiko
2026-09-10 13:46 ` [PATCH net-next v2 4/4] docs: netconsole: document rate limit feature Breno Leitao
3 siblings, 1 reply; 9+ messages in thread
From: Breno Leitao @ 2026-09-10 13:46 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: Randy Dunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, Breno Leitao,
kernel-team
A target that sets ratelimit_interval_ms runs with the ratelimit default
of 10 messages per interval, which is either too coarse or too generous
depending on how chatty the target is.
Expose it as ratelimit_burst through configfs.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
drivers/net/netconsole.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index faf8f9bbc6c0c2..10e02b7c3f9cbb 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -953,6 +953,13 @@ static ssize_t ratelimit_interval_ms_show(struct config_item *item, char *buf)
jiffies_to_msecs(READ_ONCE(nt->ratelimit.interval)));
}
+static ssize_t ratelimit_burst_show(struct config_item *item, char *buf)
+{
+ struct netconsole_target *nt = to_target(item);
+
+ return sysfs_emit(buf, "%d\n", READ_ONCE(nt->ratelimit.burst));
+}
+
/* configfs helper to display if cpu_nr sysdata feature is enabled */
static ssize_t sysdata_cpu_nr_enabled_show(struct config_item *item, char *buf)
{
@@ -1371,6 +1378,27 @@ static ssize_t ratelimit_interval_ms_store(struct config_item *item,
return count;
}
+static ssize_t ratelimit_burst_store(struct config_item *item, const char *buf,
+ size_t count)
+{
+ struct netconsole_target *nt = to_target(item);
+ unsigned int burst;
+ ssize_t ret;
+
+ ret = kstrtouint(buf, 10, &burst);
+ if (ret)
+ return ret;
+
+ if (burst > INT_MAX)
+ return -ERANGE;
+
+ dynamic_netconsole_mutex_lock();
+ WRITE_ONCE(nt->ratelimit.burst, burst);
+ dynamic_netconsole_mutex_unlock();
+
+ return count;
+}
+
struct userdatum {
struct config_item item;
char value[MAX_EXTRADATA_VALUE_LEN];
@@ -1736,6 +1764,7 @@ CONFIGFS_ATTR(, remote_mac);
CONFIGFS_ATTR(, release);
CONFIGFS_ATTR_RO(, transmit_errors);
CONFIGFS_ATTR(, ratelimit_interval_ms);
+CONFIGFS_ATTR(, ratelimit_burst);
static struct configfs_attribute *netconsole_target_attrs[] = {
&attr_enabled,
@@ -1750,6 +1779,7 @@ static struct configfs_attribute *netconsole_target_attrs[] = {
&attr_remote_mac,
&attr_transmit_errors,
&attr_ratelimit_interval_ms,
+ &attr_ratelimit_burst,
NULL,
};
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH net-next v2 3/4] netconsole: allow configuring the rate limit burst through configfs
2026-09-10 13:46 ` [PATCH net-next v2 3/4] netconsole: allow configuring the rate limit burst " Breno Leitao
@ 2026-09-12 0:06 ` netdev-bot+sashiko
0 siblings, 0 replies; 9+ messages in thread
From: netdev-bot+sashiko @ 2026-09-12 0:06 UTC (permalink / raw)
To: leitao
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, corbet,
skhan, shuah, rdunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, kernel-team
Thank you for your contribution! Sashiko AI review found 1 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 0 · Low: 1
- [Low] New user-visible configfs attribute `ratelimit_burst` (registered
in netconsole_target_attrs[] at drivers/net/netconsole.c:1782,…
--
netconsole: allow configuring the rate limit burst through configfs
This adds a writable ratelimit_burst configfs attribute to each netconsole
target so the ratelimit burst (number of messages allowed per interval) can
be tuned instead of always using the ratelimit default of 10. The new
attribute is registered in netconsole_target_attrs[].
> diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
> index faf8f9bbc6c0c..10e02b7c3f9cb 100644
> --- a/drivers/net/netconsole.c
> +++ b/drivers/net/netconsole.c
[ ... ]
> @@ -1371,6 +1378,27 @@ static ssize_t ratelimit_interval_ms_store(struct config_item *item,
> return count;
> }
>
> +static ssize_t ratelimit_burst_store(struct config_item *item, const char *buf,
> + size_t count)
> +{
> + struct netconsole_target *nt = to_target(item);
> + unsigned int burst;
> + ssize_t ret;
> +
> + ret = kstrtouint(buf, 10, &burst);
> + if (ret)
> + return ret;
> +
> + if (burst > INT_MAX)
> + return -ERANGE;
> +
> + dynamic_netconsole_mutex_lock();
> + WRITE_ONCE(nt->ratelimit.burst, burst);
> + dynamic_netconsole_mutex_unlock();
> +
> + return count;
> +}
> +
[ ... ]
> @@ -1750,6 +1779,7 @@ static struct configfs_attribute *netconsole_target_attrs[] = {
> &attr_remote_mac,
> &attr_transmit_errors,
> &attr_ratelimit_interval_ms,
> + &attr_ratelimit_burst,
> NULL,
> };
[Severity: Low]
This isn't a bug, but should the new configfs file also be described in
Documentation/networking/netconsole.rst?
At this commit the document still enumerates the target attributes with:
The interface exposes these parameters of a netconsole target to userspace:
...
transmit_errors Number of packet send errors (read-only)
so neither ratelimit_interval_ms nor the ratelimit_burst added here appears
in that table.
Things a reader cannot find documented for ratelimit_burst_store() /
ratelimit_burst_show(): the default value (DEFAULT_RATELIMIT_BURST, 10, as
set by netconsole_ratelimit_init()), the accepted range, what a burst of 0
means, and whether the value may be written while the target is enabled.
Note that the later patch in this series, "docs: netconsole: document rate
limit feature", adds a dedicated "Rate limiting" section covering both
ratelimit files, the burst default of 10, the zero semantics and the fact
that both knobs can be written while the target is enabled, so the only
remaining difference is that the ratelimit rows are not listed in the older
parameter table above. Was leaving them out of that table intentional?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260910-netcons_ratelimit-v2-0-ebf0dd91e26e%40debian.org
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH net-next v2 4/4] docs: netconsole: document rate limit feature
2026-09-10 13:46 [PATCH net-next v2 0/4] netconsole: Support messages ratelimit-ing Breno Leitao
` (2 preceding siblings ...)
2026-09-10 13:46 ` [PATCH net-next v2 3/4] netconsole: allow configuring the rate limit burst " Breno Leitao
@ 2026-09-10 13:46 ` Breno Leitao
2026-09-12 0:06 ` netdev-bot+sashiko
3 siblings, 1 reply; 9+ messages in thread
From: Breno Leitao @ 2026-09-10 13:46 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Shuah Khan
Cc: Randy Dunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, Breno Leitao,
kernel-team
Describe the per-target token bucket and the two configfs files that
drive it: ratelimit_interval_ms and ratelimit_burst.
Spell out the two properties that are not obvious from the file names.
The limit is accounted per message rather than per packet, so a message
split into several ncfrag packets is never truncated by the bucket
running dry halfway through.
Note in the message ID section that a message the bucket discards never
reaches the counter, so those drops leave no gap in the IDs.
Signed-off-by: Breno Leitao <leitao@debian.org>
---
Documentation/networking/netconsole.rst | 38 +++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
index 4ab5d7b05cf102..a9ccf798346562 100644
--- a/Documentation/networking/netconsole.rst
+++ b/Documentation/networking/netconsole.rst
@@ -177,6 +177,41 @@ You can modify these targets in runtime by creating the following targets::
cat cmdline1/remote_ip
10.0.0.3
+Rate limiting
+-------------
+
+Netconsole hands every console message to every enabled target, so a host that
+logs continuously can saturate the receiving agent. Each target carries a token
+bucket that drops messages once the configured rate is exceeded, controlled by
+two files in the target directory:
+
+ ===================== ================================================
+ ratelimit_interval_ms Length of the accounting interval, in
+ milliseconds. Zero, the default, sends
+ everything.
+ ratelimit_burst Messages allowed per interval. Defaults to
+ 10; zero drops every message once an
+ interval is set.
+ ===================== ================================================
+
+Unlike most target parameters, both knobs can be written while the target is
+enabled, which is when a flooding target most likely needs them.
+
+The limit is applied per message, not per packet, so a message big enough to be
+split into several `ncfrag` packets is either sent whole or not at all.
+
+Crash output bypasses the bucket. While an oops, BUG() or panic() is in
+progress every message is sent, whatever the limit says, so a small burst
+cannot cost you part of a crash dump.
+
+A drop leaves nothing on the wire. On an extended target it shows up as a gap
+in the sequence number the header carries; a basic target has no such marker.
+
+Capping a target at 500 messages a minute::
+
+ echo 60000 > ratelimit_interval_ms
+ echo 500 > ratelimit_burst
+
Append User Data
----------------
@@ -359,6 +394,9 @@ indicate that a message was dropped during transmission, as it may never have
been sent via netconsole. The message ID, on the other hand, is only assigned
to messages that are actually transmitted via netconsole.
+A message the target's rate limit discards is dropped before the ID is
+assigned, so those drops leave no gap in the sequence of IDs either.
+
Example::
echo "This is message #1" > /dev/kmsg
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH net-next v2 4/4] docs: netconsole: document rate limit feature
2026-09-10 13:46 ` [PATCH net-next v2 4/4] docs: netconsole: document rate limit feature Breno Leitao
@ 2026-09-12 0:06 ` netdev-bot+sashiko
0 siblings, 0 replies; 9+ messages in thread
From: netdev-bot+sashiko @ 2026-09-12 0:06 UTC (permalink / raw)
To: leitao
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, horms, corbet,
skhan, shuah, rdunlap, paulmck, gustavold, asantostc, netdev,
linux-kernel, linux-doc, linux-kselftest, kernel-team
Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:
Critical: 0 · High: 0 · Medium: 1 · Low: 1
- [Medium] The live-update path for the two rate-limit knobs does not
reset the token bucket, so the behaviour advertised by the new…
- [Low] Documentation/networking/netconsole.rst: the canonical configfs
inventory for a netconsole target ("The interface exposes these…
--
docs: netconsole: document rate limit feature
This adds a "Rate limiting" section to the netconsole documentation
describing the per-target token bucket and the two configfs files that
drive it, ratelimit_interval_ms and ratelimit_burst. It also notes that
messages dropped by the bucket never get a message ID assigned.
> diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
> index 4ab5d7b05cf10..a9ccf79834656 100644
> --- a/Documentation/networking/netconsole.rst
> +++ b/Documentation/networking/netconsole.rst
> @@ -177,6 +177,41 @@ You can modify these targets in runtime by creating the following targets::
> cat cmdline1/remote_ip
> 10.0.0.3
>
> +Rate limiting
> +-------------
> +
> +Netconsole hands every console message to every enabled target, so a host that
> +logs continuously can saturate the receiving agent. Each target carries a token
> +bucket that drops messages once the configured rate is exceeded, controlled by
> +two files in the target directory:
> +
> + ===================== ================================================
> + ratelimit_interval_ms Length of the accounting interval, in
> + milliseconds. Zero, the default, sends
> + everything.
> + ratelimit_burst Messages allowed per interval. Defaults to
> + 10; zero drops every message once an
> + interval is set.
> + ===================== ================================================
> +
> +Unlike most target parameters, both knobs can be written while the target is
> +enabled, which is when a flooding target most likely needs them.
[Severity: Low]
Should the target parameter table earlier in this file be extended too?
That table is introduced with "The interface exposes these parameters of a
netconsole target to userspace" and it does list read-only siblings such as
transmit_errors, but it gains no entry for ratelimit_interval_ms or
ratelimit_burst, even though netconsole_target_attrs[] in
drivers/net/netconsole.c registers them right next to attr_transmit_errors.
The sentence directly under that table also seems to conflict with the new
paragraph above:
The "enabled" attribute is also used to control whether the parameters of
a target can be updated or not -- you can modify the parameters of only
disabled targets (i.e. if "enabled" is 0).
Neither ratelimit_interval_ms_store() nor ratelimit_burst_store() has the
nt->state == STATE_ENABLED test that, for example, remote_port_store() uses
before returning -EINVAL with "target (%s) is enabled, disable to update
parameters". Would it be worth qualifying that blanket rule so the document
does not describe the same ABI in two contradictory ways?
> +
> +The limit is applied per message, not per packet, so a message big enough to be
> +split into several `ncfrag` packets is either sent whole or not at all.
> +
> +Crash output bypasses the bucket. While an oops, BUG() or panic() is in
> +progress every message is sent, whatever the limit says, so a small burst
> +cannot cost you part of a crash dump.
> +
> +A drop leaves nothing on the wire. On an extended target it shows up as a gap
> +in the sequence number the header carries; a basic target has no such marker.
> +
> +Capping a target at 500 messages a minute::
> +
> + echo 60000 > ratelimit_interval_ms
> + echo 500 > ratelimit_burst
[Severity: Medium]
Does this two-step example behave as described on the flooding target the
section is aimed at?
ratelimit_interval_ms_store() and ratelimit_burst_store() in
drivers/net/netconsole.c only publish the new values:
dynamic_netconsole_mutex_lock();
WRITE_ONCE(nt->ratelimit.interval, jifs);
dynamic_netconsole_mutex_unlock();
dynamic_netconsole_mutex_lock();
WRITE_ONCE(nt->ratelimit.burst, burst);
dynamic_netconsole_mutex_unlock();
In lib/ratelimit.c, ___ratelimit() re-seeds the live allowance from
rs->burst only on first use or once the interval has expired:
if (!(rs->flags & RATELIMIT_INITIALIZED)) {
rs->begin = jiffies;
rs->flags |= RATELIMIT_INITIALIZED;
atomic_set(&rs->rs_n_left, rs->burst);
}
if (time_is_before_jiffies(rs->begin + interval)) {
...
atomic_set(&rs->rs_n_left, rs->burst);
So after the first echo starts a 60 s interval with the default burst of 10,
a flooding target drains those 10 tokens before the second shell command
runs. Once burst=500 is stored, rs_n_left stays at 0 and every message is
dropped for the rest of the 60 s interval, while cat ratelimit_burst reports
500. Lowering the burst mid-interval leaves the larger allowance spendable,
and the next interval is still measured from the old rs->begin.
Should the stores go through ratelimit_state_reset_interval() in
include/linux/ratelimit.h, which takes rs->lock, clears
RATELIMIT_INITIALIZED, does atomic_set(&rs->rs_n_left, rs->burst) and resets
the missed counter? Otherwise, should this section say that a change only
takes effect at the next interval boundary?
There is also the plain load of rs->burst in the two atomic_set() calls in
___ratelimit(), which run under rs->lock. The configfs writers hold only
dynamic_netconsole_mutex, which the console write path
netconsole_write() -> netconsole_ratelimited() -> __ratelimit() never takes,
so the locksets do not intersect. Is that concurrent plain read against
WRITE_ONCE() something KCSAN would flag?
>
> Append User Data
> ----------------
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260910-netcons_ratelimit-v2-0-ebf0dd91e26e%40debian.org
^ permalink raw reply [flat|nested] 9+ messages in thread