[PATCH v4 1/2] xen/console: correct leaky-bucket rate limiter

[email protected] Wed, 29 Jul 2026 00:25:19 -0700
Newsgroups gmane.comp.emulators.xen.devel
Message-ID <[email protected]>
From: Denis Mukhin <[email protected]> 

Use existing 'ratelimit_ms' and 'ratelimit_burst' variables in
do_printk_ratelimit() instead of hardcoded values 5000 and 10 respectively.

Ensure rate limiter is disabled if either 'ratelimit_ms' or 'ratelimit_burst'
is 0.

Account for integer overflow in the rate-limiter logic.

Signed-off-by: Denis Mukhin <[email protected]>
---
Changes since v3:
- fixed types
- fixed integer division logic - I used DIM_MUL2() from xvmalloc.h
  I hope this is fine given another pending patch which will include xvmalloc.h
  for heap allocations
- fixed potential problem w/ overflow of toks (introduced elapsed)
- fixed potential problem with toks == 0 which is also "uninitialized"
  state.
---
 xen/drivers/char/console.c | 37 ++++++++++++++++++++++++++++++-------
 1 file changed, 30 insertions(+), 7 deletions(-)

diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index ea4e3ff34178..76a1681670c1 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -33,6 +33,7 @@
 #include <asm/setup.h>
 #include <xen/sections.h>
 #include <xen/consoled.h>
+#include <xen/xvmalloc.h>
 
 #ifdef CONFIG_X86
 #include <asm/guest.h>
@@ -1286,21 +1287,43 @@ bool __printk_ratelimit(unsigned int ratelimit_ms,
                         unsigned int ratelimit_burst)
 {
     static DEFINE_SPINLOCK(ratelimit_lock);
-    static unsigned long toks = 10 * 5 * 1000;
+    static unsigned long toks;
     static unsigned long last_msg;
     static unsigned int missed;
+    static bool initialized;
+    unsigned long limit;
     unsigned long flags;
-    unsigned long long now = NOW(); /* ns */
     unsigned long ms;
+    s_time_t now;
 
-    do_div(now, 1000000);
-    ms = (unsigned long)now;
+    if ( !ratelimit_ms || !ratelimit_burst )
+        return true;
+
+    limit = DIM_MUL2(ratelimit_burst, ratelimit_ms);
+
+    now = NOW(); /* ns */
+    do_div(now, MILLISECS(1));
+    ms = now;
 
     spin_lock_irqsave(&ratelimit_lock, flags);
-    toks += ms - last_msg;
+
+    if ( initialized )
+    {
+        unsigned long elapsed = ms - last_msg;
+
+        if ( toks >= limit || elapsed >= limit - toks )
+            toks = limit;
+        else
+            toks += elapsed;
+    }
+    else
+    {
+        toks = limit;
+        initialized = true;
+    }
+
     last_msg = ms;
-    if ( toks > (ratelimit_burst * ratelimit_ms))
-        toks = ratelimit_burst * ratelimit_ms;
+
     if ( toks >= ratelimit_ms )
     {
         unsigned int lost = missed;
-- 
2.54.0