From: Anton Blanchard <anton@samba.org>

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:

_