Why Rate Limiting Matters
Imagine your driver handles a hardware interrupt that fires 1000 times per second. If you have a pr_warn() inside that interrupt handler, you will produce 1000 log messages every single second. The kernel ring buffer will fill up and start overwriting older, potentially important messages. Log files on disk will grow at an alarming rate, wasting flash storage on embedded systems.
Even worse: on systems using legacy consoles, each printk() call tries to acquire a lock and push characters to the console. At 1000 calls per second this can trigger the kernel’s softlockup watchdog and cause false lockup warnings — or even a real lockup.
The solution is rate-limited printk. Linux provides macros that allow a burst of messages at first, then quietly drop further messages until a time window resets, and then tells you how many were dropped.
The rate-limiting algorithm uses two parameters: a burst count (how many messages are allowed immediately) and a time interval (the window after which the counter resets).
When the window resets and the next message is allowed, the kernel also prints: “myfunction: N callbacks suppressed” so you know how many were dropped.
Every level has a rate-limited version. They are named by adding _ratelimited:
#include <linux/printk.h>
/* General rate-limited printk */
printk_ratelimited(KERN_WARNING "message\n");
/* Level-specific shortcuts (recommended) */
pr_emerg_ratelimited(fmt, ...);
pr_alert_ratelimited(fmt, ...);
pr_crit_ratelimited(fmt, ...);
pr_err_ratelimited(fmt, ...);
pr_warn_ratelimited(fmt, ...);
pr_notice_ratelimited(fmt, ...);
pr_info_ratelimited(fmt, ...);
pr_debug_ratelimited(fmt, ...); /* also tied to dynamic debug */
/* Device driver versions */
dev_warn_ratelimited(dev, fmt, ...);
dev_err_ratelimited(dev, fmt, ...);
The defaults baked into these macros are:
- Burst: 10 messages allowed immediately
- Window: 5 seconds before the counter resets
Each macro call site has its own independent counter. If you have two different pr_warn_ratelimited() calls in your code, each one gets its own 10-per-5s budget.
/*
* Example: a network driver receive function called at high frequency.
* Without rate limiting this would flood the log.
*/
static int mynic_rx(struct mynic_dev *dev, int budget)
{
u32 status = read_reg(dev, REG_RX_STATUS);
if (status & RX_ERR_OVERFLOW) {
/* This might fire thousands of times per second — rate limit it */
pr_warn_ratelimited("RX FIFO overflow detected, status=0x%x\n", status);
}
if (status & RX_ERR_CRC) {
/* Separate rate limit from the overflow warning above */
pr_warn_ratelimited("CRC error on received packet\n");
}
return process_packets(dev, budget);
}
Sometimes you want to log a message exactly once and never again — for example, a “deprecated API” warning. Linux provides _once variants for this.
/* Prints exactly once per boot, then never again */
pr_warn_once("Using legacy ioctl interface — please update your application\n");
pr_info_once("Firmware version %s loaded\n", fw_version);
/* Device driver version */
dev_warn_once(&pdev->dev, "Using fallback clock source\n");
Internally, each _once macro uses a static bool flag that is set to true after the first print. Since it is a static variable, it survives for the lifetime of the module. The flag is per-callsite — two different pr_info_once() calls in your code each track independently.
| Variant | Behaviour | Best For |
|---|---|---|
| pr_warn() | Every call prints | Low-frequency error paths |
| pr_warn_ratelimited() | Up to 10 per 5 seconds, drops excess | Interrupt handlers, high-frequency paths |
| pr_warn_once() | Exactly one time, never again | Deprecation notices, one-time state messages |
You may find references to these two files in older tutorials:
/proc/sys/kernel/printk_ratelimit # default: 5
/proc/sys/kernel/printk_ratelimit_burst # default: 10
Here is the important truth that most tutorials get wrong: these sysctl files do NOT control pr_warn_ratelimited() or printk_ratelimited().
These sysctl files only control a single global ratelimit state used by the legacy printk_ratelimit() function — an old, deprecated function that the kernel itself marks as “don’t use.” Modern rate-limited macros (pr_*_ratelimited, dev_*_ratelimited) each use their own private per-callsite state with the defaults (10 burst / 5 seconds) baked in at compile time. Changing the sysctl files has no effect on them.
Controlled by sysctl files.
Do not use in new code.
pr_err_ratelimited()
dev_warn_ratelimited()
private state (10 burst / 5s).
Sysctl has no effect.
dev_info(), dev_err(), dev_warn()
dev_dbg() for debug
dev_err_probe() in probe()
Define pr_fmt() first
pr_info(), pr_err(), pr_warn()
pr_debug() for debug
_ratelimited variant: pr_warn_ratelimited(), dev_err_ratelimited()
_once variant: pr_warn_once(), dev_info_once()
dev_dbg() — enabled via dynamic debug at runtimeNon-driver code:
pr_debug()Never want in dynamic debug:
pr_devel() with -DDEBUG
print_hex_dump_debug() — also controlled by dynamic debug
The printk API (the macros and log levels you use every day) is unchanged in Linux 6.x. What changed is deep inside — how messages reach the console hardware. This matters to you if you care about latency or are working on PREEMPT_RT.
The old design used a single global lock called console_sem that every CPU had to compete for to push characters to the console. On a system with a slow serial console (9600 baud), one printk call could hold this lock for milliseconds, blocking all other CPUs from printing. This made printk() in hot paths dangerous on real-time kernels.
The nbcon (Non-Blocking CONsole) infrastructure was completed and merged in Linux 6.12. It introduced per-console locking with atomic handover, dedicated printing kthreads, and a new write_thread() / write_atomic() callback model for console drivers. This was the last remaining blocker for merging PREEMPT_RT into the mainline kernel — also merged in 6.12.
What this means for you as a module developer: If your kernel is 6.12 or later with nbcon-enabled consoles, the historical advice “avoid printk in interrupt handlers” is less critical than before. However, rate limiting is still good practice to protect the ring buffer and log files.
Here is a realistic example module that demonstrates correct use of log levels, pr_fmt(), pr_debug(), and rate limiting together.
/*
* demo_logging.c — demonstrates printk best practices
* Tested on Linux 6.x
*
* Define pr_fmt BEFORE any includes so all pr_*() calls get the prefix.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("printk / logging demo for EmbeddedPathashala");
MODULE_AUTHOR("EmbeddedPathashala");
static struct timer_list demo_timer;
static int fire_count = 0;
/*
* This function is called repeatedly by a timer.
* It shows rate limiting protecting a high-frequency path.
*/
static void timer_callback(struct timer_list *t)
{
fire_count++;
/* This would normally flood the log — rate limited to 10 per 5s */
pr_warn_ratelimited("timer fired (count=%d) — rate-limited warning\n",
fire_count);
/* Debug message — visible only when dynamic debug enables it */
pr_debug("timer internal state: count=%d\n", fire_count);
/* Reschedule after 10ms — fires 100 times per second */
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(10));
}
static int __init demo_init(void)
{
/* KERN_INFO — normal startup message */
pr_info("module loading (version 1.0)\n");
/* One-time warning — shows once no matter how many times init runs */
pr_info_once("This message will only appear once per boot\n");
/* Set up the high-frequency timer */
timer_setup(&demo_timer, timer_callback, 0);
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(100));
pr_info("timer started — will fire 100 times/second\n");
pr_info("run: dmesg -w to watch rate-limited output\n");
pr_info("run: echo 'module demo_logging +p' > /sys/kernel/debug/dynamic_debug/control\n");
pr_info(" to enable pr_debug() messages\n");
return 0;
}
static void __exit demo_exit(void)
{
del_timer_sync(&demo_timer);
pr_info("module unloaded after %d timer firings\n", fire_count);
}
module_init(demo_init);
module_exit(demo_exit);
To build, create a Makefile:
obj-m := demo_logging.o
# Enable pr_debug() statically (only needed if CONFIG_DYNAMIC_DEBUG is not set)
# ccflags-y += -DDEBUG
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Load and test:
# Build and load
make
sudo insmod demo_logging.ko
# Watch rate-limited output (you'll see the "N callbacks suppressed" messages)
dmesg -w
# Enable the pr_debug() messages at runtime
echo 'module demo_logging +p' | sudo tee /sys/kernel/debug/dynamic_debug/control
# Unload
sudo rmmod demo_logging
dmesg | tail -5
Interview Questions & Answers
On systems with legacy consoles, printk() tries to acquire the console lock and push all buffered messages to the console driver synchronously. If a hardware interrupt fires 1000 times per second and each call holds the console lock even briefly, it can prevent other CPUs from printing, trigger the softlockup watchdog, and in worst cases cause a real lockup. The correct approach is to use pr_warn_ratelimited() or dev_warn_ratelimited(), which limits output to 10 messages per 5 seconds from that callsite.
No. These sysctl files control only the legacy printk_ratelimit() function, which is deprecated. Modern macros like pr_warn_ratelimited() use per-callsite private state with defaults of 10 messages per 5 seconds baked in at compile time. Changing the sysctl files has no effect on these modern macros.
pr_warn_ratelimited() allows up to 10 messages per 5 seconds and then drops excess, repeating this cycle indefinitely. pr_warn_once() prints exactly once for the entire life of the module — it sets a static flag on first print and never prints again. Use ratelimited for ongoing conditions you want to know about but in a controlled way, and use once for one-time events like deprecation notices.
nbcon (Non-Blocking CONsole) replaces the global console_sem lock with per-console locking and dedicated printing kthreads. In the old design, any CPU calling printk() would hold a global lock while pushing characters to the console hardware, blocking all other CPUs. nbcon makes printing non-blocking for the caller — messages go into the ring buffer immediately and a kthread flushes them to the console asynchronously. This eliminated the last blocker for merging PREEMPT_RT (real-time Linux) into the mainline kernel in Linux 6.12.
Each expansion of the printk_ratelimited() macro creates a static DEFINE_RATELIMIT_STATE variable inside the macro. Because it is declared static, it lives at that specific location in code and is not shared with other callsites. Two different pr_warn_ratelimited() calls in your file each get their own ratelimit_state with their own burst counter and timing window, so they are completely independent of each other.
Free Linux Kernel Programming Course
All chapters are free. No signup required. Learn embedded Linux and device driver development at EmbeddedPathashala.
Course Index ← Previous Chapter