From: Anton Blanchard Generate a global printk rate-limiting function, printk_ratelimit(). Also, use it in the page allocator warning code. Also add a dump_stack to that code. Later, we need to switch net_ratelimit() over to use printk_ratelimit(). Documentation/sysctl/kernel.txt | 19 ++++++++++++++++++ include/linux/kernel.h | 2 + include/linux/sysctl.h | 2 + kernel/printk.c | 42 ++++++++++++++++++++++++++++++++++++++++ kernel/sysctl.c | 18 +++++++++++++++++ mm/page_alloc.c | 5 ++-- 6 files changed, 86 insertions(+), 2 deletions(-) diff -puN Documentation/sysctl/kernel.txt~printk_ratelimit Documentation/sysctl/kernel.txt --- 25/Documentation/sysctl/kernel.txt~printk_ratelimit 2004-01-01 15:29:51.000000000 -0800 +++ 25-akpm/Documentation/sysctl/kernel.txt 2004-01-01 15:29:51.000000000 -0800 @@ -251,6 +251,25 @@ the different loglevels. ============================================================== +printk_ratelimit: + +Some warning messages are rate limited. printk_ratelimit specifies +the minimum length of time between these messages, by default we +allow one every 5 seconds. + +A value of 0 will disable rate limiting. + +============================================================== + +printk_ratelimit_burst: + +While long term we enforce one message per printk_ratelimit +seconds, we do allow a burst of messages to pass through. +printk_ratelimit_burst specifies the number of messages we can +send before ratelimiting kicks in. + +============================================================== + reboot-cmd: (Sparc only) ??? This seems to be a way to give an argument to the Sparc diff -puN include/linux/kernel.h~printk_ratelimit include/linux/kernel.h --- 25/include/linux/kernel.h~printk_ratelimit 2004-01-01 15:29:51.000000000 -0800 +++ 25-akpm/include/linux/kernel.h 2004-01-01 15:29:51.000000000 -0800 @@ -89,6 +89,8 @@ asmlinkage int printk(const char * fmt, unsigned long int_sqrt(unsigned long); +extern int printk_ratelimit(void); + static inline void console_silent(void) { console_loglevel = 0; diff -puN include/linux/sysctl.h~printk_ratelimit include/linux/sysctl.h --- 25/include/linux/sysctl.h~printk_ratelimit 2004-01-01 15:29:51.000000000 -0800 +++ 25-akpm/include/linux/sysctl.h 2004-01-01 15:29:51.000000000 -0800 @@ -127,6 +127,8 @@ enum KERN_PANIC_ON_OOPS=57, /* int: whether we will panic on an oops */ KERN_HPPA_PWRSW=58, /* int: hppa soft-power enable */ KERN_HPPA_UNALIGNED=59, /* int: hppa unaligned-trap enable */ + KERN_PRINTK_RATELIMIT=60, /* int: tune printk ratelimiting */ + KERN_PRINTK_RATELIMIT_BURST=61, /* int: tune printk ratelimiting */ }; diff -puN kernel/printk.c~printk_ratelimit kernel/printk.c --- 25/kernel/printk.c~printk_ratelimit 2004-01-01 15:29:51.000000000 -0800 +++ 25-akpm/kernel/printk.c 2004-01-01 15:29:51.000000000 -0800 @@ -763,3 +763,45 @@ void tty_write_message(struct tty_struct tty->driver->write(tty, 0, msg, strlen(msg)); return; } + +/* minimum time in jiffies between messages */ +int printk_ratelimit_jiffies = 5*HZ; + +/* number of messages we send before ratelimiting */ +int printk_ratelimit_burst = 10; + +/* + * printk rate limiting, lifted from the networking subsystem. + * + * This enforces a rate limit: not more than one kernel message + * every printk_ratelimit_jiffies to make a denial-of-service + * attack impossible. + */ +int printk_ratelimit(void) +{ + static spinlock_t ratelimit_lock = SPIN_LOCK_UNLOCKED; + static unsigned long toks = 10*5*HZ; + static unsigned long last_msg; + static int missed; + unsigned long flags; + unsigned long now = jiffies; + + spin_lock_irqsave(&ratelimit_lock, flags); + toks += now - last_msg; + last_msg = now; + if (toks > (printk_ratelimit_burst * printk_ratelimit_jiffies)) + toks = printk_ratelimit_burst * printk_ratelimit_jiffies; + if (toks >= printk_ratelimit_jiffies) { + int lost = missed; + missed = 0; + toks -= printk_ratelimit_jiffies; + spin_unlock_irqrestore(&ratelimit_lock, flags); + if (lost) + printk(KERN_WARNING "printk: %d messages suppressed.\n", lost); + return 1; + } + missed++; + spin_unlock_irqrestore(&ratelimit_lock, flags); + return 0; +} +EXPORT_SYMBOL(printk_ratelimit); diff -puN kernel/sysctl.c~printk_ratelimit kernel/sysctl.c --- 25/kernel/sysctl.c~printk_ratelimit 2004-01-01 15:29:51.000000000 -0800 +++ 25-akpm/kernel/sysctl.c 2004-01-01 15:29:51.000000000 -0800 @@ -61,6 +61,8 @@ extern int cad_pid; extern int pid_max; extern int sysctl_lower_zone_protection; extern int min_free_kbytes; +extern int printk_ratelimit_jiffies; +extern int printk_ratelimit_burst; /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */ static int maxolduid = 65535; @@ -580,6 +582,22 @@ static ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, + { + .ctl_name = KERN_PRINTK_RATELIMIT, + .procname = "printk_ratelimit", + .data = &printk_ratelimit_jiffies, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_jiffies, + }, + { + .ctl_name = KERN_PRINTK_RATELIMIT_BURST, + .procname = "printk_ratelimit_burst", + .data = &printk_ratelimit_burst, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, { .ctl_name = 0 } }; diff -puN mm/page_alloc.c~printk_ratelimit mm/page_alloc.c --- 25/mm/page_alloc.c~printk_ratelimit 2004-01-01 15:29:51.000000000 -0800 +++ 25-akpm/mm/page_alloc.c 2004-01-01 15:29:51.000000000 -0800 @@ -669,10 +669,11 @@ rebalance: } nopage: - if (!(gfp_mask & __GFP_NOWARN)) { - printk("%s: page allocation failure." + if (!(gfp_mask & __GFP_NOWARN) && printk_ratelimit()) { + printk(KERN_WARNING "%s: page allocation failure." " order:%d, mode:0x%x\n", p->comm, order, gfp_mask); + dump_stack(); } return NULL; got_pg: _