Commit 717115e1 authored by Dave Young's avatar Dave Young Committed by Linus Torvalds

printk ratelimiting rewrite

All ratelimit user use same jiffies and burst params, so some messages
(callbacks) will be lost.

For example:
a call printk_ratelimit(5 * HZ, 1)
b call printk_ratelimit(5 * HZ, 1) before the 5*HZ timeout of a, then b will
will be supressed.

- rewrite __ratelimit, and use a ratelimit_state as parameter.  Thanks for
  hints from andrew.

- Add WARN_ON_RATELIMIT, update rcupreempt.h

- remove __printk_ratelimit

- use __ratelimit in net_ratelimit
Signed-off-by: default avatarDave Young <hidave.darkstar@gmail.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: "Paul E. McKenney" <paulmck@us.ibm.com>
Cc: Dave Young <hidave.darkstar@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 2711b793
...@@ -97,6 +97,9 @@ extern void warn_slowpath(const char *file, const int line, ...@@ -97,6 +97,9 @@ extern void warn_slowpath(const char *file, const int line,
unlikely(__ret_warn_once); \ unlikely(__ret_warn_once); \
}) })
#define WARN_ON_RATELIMIT(condition, state) \
WARN_ON((condition) && __ratelimit(state))
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
# define WARN_ON_SMP(x) WARN_ON(x) # define WARN_ON_SMP(x) WARN_ON(x)
#else #else
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/typecheck.h> #include <linux/typecheck.h>
#include <linux/ratelimit.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/bug.h> #include <asm/bug.h>
...@@ -189,11 +190,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) ...@@ -189,11 +190,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
asmlinkage int printk(const char * fmt, ...) asmlinkage int printk(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2))) __cold; __attribute__ ((format (printf, 1, 2))) __cold;
extern int printk_ratelimit_jiffies; extern struct ratelimit_state printk_ratelimit_state;
extern int printk_ratelimit_burst;
extern int printk_ratelimit(void); extern int printk_ratelimit(void);
extern int __ratelimit(int ratelimit_jiffies, int ratelimit_burst);
extern int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst);
extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
unsigned int interval_msec); unsigned int interval_msec);
#else #else
...@@ -204,8 +202,6 @@ static inline int printk(const char *s, ...) ...@@ -204,8 +202,6 @@ static inline int printk(const char *s, ...)
__attribute__ ((format (printf, 1, 2))); __attribute__ ((format (printf, 1, 2)));
static inline int __cold printk(const char *s, ...) { return 0; } static inline int __cold printk(const char *s, ...) { return 0; }
static inline int printk_ratelimit(void) { return 0; } static inline int printk_ratelimit(void) { return 0; }
static inline int __printk_ratelimit(int ratelimit_jiffies, \
int ratelimit_burst) { return 0; }
static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \ static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \
unsigned int interval_msec) \ unsigned int interval_msec) \
{ return false; } { return false; }
......
...@@ -351,8 +351,7 @@ static const struct proto_ops name##_ops = { \ ...@@ -351,8 +351,7 @@ static const struct proto_ops name##_ops = { \
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
#include <linux/sysctl.h> #include <linux/sysctl.h>
extern int net_msg_cost; extern struct ratelimit_state net_ratelimit_state;
extern int net_msg_burst;
#endif #endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
#ifndef _LINUX_RATELIMIT_H
#define _LINUX_RATELIMIT_H
#include <linux/param.h>
#define DEFAULT_RATELIMIT_INTERVAL (5 * HZ)
#define DEFAULT_RATELIMIT_BURST 10
struct ratelimit_state {
int interval;
int burst;
int printed;
int missed;
unsigned long begin;
};
#define DEFINE_RATELIMIT_STATE(name, interval, burst) \
struct ratelimit_state name = {interval, burst,}
extern int __ratelimit(struct ratelimit_state *rs);
static inline int ratelimit(void)
{
static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
return __ratelimit(&rs);
}
#endif
...@@ -115,16 +115,21 @@ DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched); ...@@ -115,16 +115,21 @@ DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);
static inline void rcu_enter_nohz(void) static inline void rcu_enter_nohz(void)
{ {
static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1);
smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
__get_cpu_var(rcu_dyntick_sched).dynticks++; __get_cpu_var(rcu_dyntick_sched).dynticks++;
WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1); WARN_ON_RATELIMIT(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1, &rs);
} }
static inline void rcu_exit_nohz(void) static inline void rcu_exit_nohz(void)
{ {
static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1);
smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
__get_cpu_var(rcu_dyntick_sched).dynticks++; __get_cpu_var(rcu_dyntick_sched).dynticks++;
WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1)); WARN_ON_RATELIMIT(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1),
&rs);
} }
#else /* CONFIG_NO_HZ */ #else /* CONFIG_NO_HZ */
......
...@@ -1308,6 +1308,8 @@ void tty_write_message(struct tty_struct *tty, char *msg) ...@@ -1308,6 +1308,8 @@ void tty_write_message(struct tty_struct *tty, char *msg)
} }
#if defined CONFIG_PRINTK #if defined CONFIG_PRINTK
DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10);
/* /*
* printk rate limiting, lifted from the networking subsystem. * printk rate limiting, lifted from the networking subsystem.
* *
...@@ -1315,22 +1317,9 @@ void tty_write_message(struct tty_struct *tty, char *msg) ...@@ -1315,22 +1317,9 @@ void tty_write_message(struct tty_struct *tty, char *msg)
* every printk_ratelimit_jiffies to make a denial-of-service * every printk_ratelimit_jiffies to make a denial-of-service
* attack impossible. * attack impossible.
*/ */
int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst)
{
return __ratelimit(ratelimit_jiffies, ratelimit_burst);
}
EXPORT_SYMBOL(__printk_ratelimit);
/* minimum time in jiffies between messages */
int printk_ratelimit_jiffies = 5 * HZ;
/* number of messages we send before ratelimiting */
int printk_ratelimit_burst = 10;
int printk_ratelimit(void) int printk_ratelimit(void)
{ {
return __printk_ratelimit(printk_ratelimit_jiffies, return __ratelimit(&printk_ratelimit_state);
printk_ratelimit_burst);
} }
EXPORT_SYMBOL(printk_ratelimit); EXPORT_SYMBOL(printk_ratelimit);
......
...@@ -624,7 +624,7 @@ static struct ctl_table kern_table[] = { ...@@ -624,7 +624,7 @@ static struct ctl_table kern_table[] = {
{ {
.ctl_name = KERN_PRINTK_RATELIMIT, .ctl_name = KERN_PRINTK_RATELIMIT,
.procname = "printk_ratelimit", .procname = "printk_ratelimit",
.data = &printk_ratelimit_jiffies, .data = &printk_ratelimit_state.interval,
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec_jiffies, .proc_handler = &proc_dointvec_jiffies,
...@@ -633,7 +633,7 @@ static struct ctl_table kern_table[] = { ...@@ -633,7 +633,7 @@ static struct ctl_table kern_table[] = {
{ {
.ctl_name = KERN_PRINTK_RATELIMIT_BURST, .ctl_name = KERN_PRINTK_RATELIMIT_BURST,
.procname = "printk_ratelimit_burst", .procname = "printk_ratelimit_burst",
.data = &printk_ratelimit_burst, .data = &printk_ratelimit_state.burst,
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec,
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
* *
* Isolated from kernel/printk.c by Dave Young <hidave.darkstar@gmail.com> * Isolated from kernel/printk.c by Dave Young <hidave.darkstar@gmail.com>
* *
* 2008-05-01 rewrite the function and use a ratelimit_state data struct as
* parameter. Now every user can use their own standalone ratelimit_state.
*
* This file is released under the GPLv2. * This file is released under the GPLv2.
* *
*/ */
...@@ -11,41 +14,43 @@ ...@@ -11,41 +14,43 @@
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/module.h> #include <linux/module.h>
static DEFINE_SPINLOCK(ratelimit_lock);
static unsigned long flags;
/* /*
* __ratelimit - rate limiting * __ratelimit - rate limiting
* @ratelimit_jiffies: minimum time in jiffies between two callbacks * @rs: ratelimit_state data
* @ratelimit_burst: number of callbacks we do before ratelimiting
* *
* This enforces a rate limit: not more than @ratelimit_burst callbacks * This enforces a rate limit: not more than @rs->ratelimit_burst callbacks
* in every ratelimit_jiffies * in every @rs->ratelimit_jiffies
*/ */
int __ratelimit(int ratelimit_jiffies, int ratelimit_burst) int __ratelimit(struct ratelimit_state *rs)
{ {
static DEFINE_SPINLOCK(ratelimit_lock); if (!rs->interval)
static unsigned toks = 10 * 5 * HZ; return 1;
static unsigned long last_msg;
static int missed;
unsigned long flags;
unsigned long now = jiffies;
spin_lock_irqsave(&ratelimit_lock, flags); spin_lock_irqsave(&ratelimit_lock, flags);
toks += now - last_msg; if (!rs->begin)
last_msg = now; rs->begin = jiffies;
if (toks > (ratelimit_burst * ratelimit_jiffies))
toks = ratelimit_burst * ratelimit_jiffies;
if (toks >= ratelimit_jiffies) {
int lost = missed;
missed = 0; if (time_is_before_jiffies(rs->begin + rs->interval)) {
toks -= ratelimit_jiffies; if (rs->missed)
spin_unlock_irqrestore(&ratelimit_lock, flags); printk(KERN_WARNING "%s: %d callbacks suppressed\n",
if (lost) __func__, rs->missed);
printk(KERN_WARNING "%s: %d messages suppressed\n", rs->begin = 0;
__func__, lost); rs->printed = 0;
return 1; rs->missed = 0;
} }
missed++; if (rs->burst && rs->burst > rs->printed)
goto print;
rs->missed++;
spin_unlock_irqrestore(&ratelimit_lock, flags); spin_unlock_irqrestore(&ratelimit_lock, flags);
return 0; return 0;
print:
rs->printed++;
spin_unlock_irqrestore(&ratelimit_lock, flags);
return 1;
} }
EXPORT_SYMBOL(__ratelimit); EXPORT_SYMBOL(__ratelimit);
...@@ -67,7 +67,7 @@ static struct ctl_table net_core_table[] = { ...@@ -67,7 +67,7 @@ static struct ctl_table net_core_table[] = {
{ {
.ctl_name = NET_CORE_MSG_COST, .ctl_name = NET_CORE_MSG_COST,
.procname = "message_cost", .procname = "message_cost",
.data = &net_msg_cost, .data = &net_ratelimit_state.interval,
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec_jiffies, .proc_handler = &proc_dointvec_jiffies,
...@@ -76,7 +76,7 @@ static struct ctl_table net_core_table[] = { ...@@ -76,7 +76,7 @@ static struct ctl_table net_core_table[] = {
{ {
.ctl_name = NET_CORE_MSG_BURST, .ctl_name = NET_CORE_MSG_BURST,
.procname = "message_burst", .procname = "message_burst",
.data = &net_msg_burst, .data = &net_ratelimit_state.burst,
.maxlen = sizeof(int), .maxlen = sizeof(int),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec,
......
...@@ -31,17 +31,16 @@ ...@@ -31,17 +31,16 @@
#include <asm/system.h> #include <asm/system.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
int net_msg_cost __read_mostly = 5*HZ;
int net_msg_burst __read_mostly = 10;
int net_msg_warn __read_mostly = 1; int net_msg_warn __read_mostly = 1;
EXPORT_SYMBOL(net_msg_warn); EXPORT_SYMBOL(net_msg_warn);
DEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10);
/* /*
* All net warning printk()s should be guarded by this function. * All net warning printk()s should be guarded by this function.
*/ */
int net_ratelimit(void) int net_ratelimit(void)
{ {
return __printk_ratelimit(net_msg_cost, net_msg_burst); return __ratelimit(&net_ratelimit_state);
} }
EXPORT_SYMBOL(net_ratelimit); EXPORT_SYMBOL(net_ratelimit);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment