Skip to content

Commit

Permalink
printk ratelimiting rewrite
Browse files Browse the repository at this point in the history
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: Dave 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: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
hidave authored and torvalds committed Jul 25, 2008
1 parent 2711b79 commit 717115e
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 56 deletions.
3 changes: 3 additions & 0 deletions include/asm-generic/bug.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ extern void warn_slowpath(const char *file, const int line,
unlikely(__ret_warn_once); \
})

#define WARN_ON_RATELIMIT(condition, state) \
WARN_ON((condition) && __ratelimit(state))

#ifdef CONFIG_SMP
# define WARN_ON_SMP(x) WARN_ON(x)
#else
Expand Down
8 changes: 2 additions & 6 deletions include/linux/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/bitops.h>
#include <linux/log2.h>
#include <linux/typecheck.h>
#include <linux/ratelimit.h>
#include <asm/byteorder.h>
#include <asm/bug.h>

Expand Down Expand Up @@ -189,11 +190,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
asmlinkage int printk(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2))) __cold;

extern int printk_ratelimit_jiffies;
extern int printk_ratelimit_burst;
extern struct ratelimit_state printk_ratelimit_state;
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,
unsigned int interval_msec);
#else
Expand All @@ -204,8 +202,6 @@ static inline int printk(const char *s, ...)
__attribute__ ((format (printf, 1, 2)));
static inline int __cold printk(const char *s, ...) { 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, \
unsigned int interval_msec) \
{ return false; }
Expand Down
3 changes: 1 addition & 2 deletions include/linux/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,7 @@ static const struct proto_ops name##_ops = { \

#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
extern int net_msg_cost;
extern int net_msg_burst;
extern struct ratelimit_state net_ratelimit_state;
#endif

#endif /* __KERNEL__ */
Expand Down
27 changes: 27 additions & 0 deletions include/linux/ratelimit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#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
9 changes: 7 additions & 2 deletions include/linux/rcupreempt.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,21 @@ DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);

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 */
__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 DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1);

smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
__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 */
Expand Down
17 changes: 3 additions & 14 deletions kernel/printk.c
Original file line number Diff line number Diff line change
Expand Up @@ -1308,29 +1308,18 @@ void tty_write_message(struct tty_struct *tty, char *msg)
}

#if defined CONFIG_PRINTK

DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 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(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)
{
return __printk_ratelimit(printk_ratelimit_jiffies,
printk_ratelimit_burst);
return __ratelimit(&printk_ratelimit_state);
}
EXPORT_SYMBOL(printk_ratelimit);

Expand Down
4 changes: 2 additions & 2 deletions kernel/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ static struct ctl_table kern_table[] = {
{
.ctl_name = KERN_PRINTK_RATELIMIT,
.procname = "printk_ratelimit",
.data = &printk_ratelimit_jiffies,
.data = &printk_ratelimit_state.interval,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
Expand All @@ -633,7 +633,7 @@ static struct ctl_table kern_table[] = {
{
.ctl_name = KERN_PRINTK_RATELIMIT_BURST,
.procname = "printk_ratelimit_burst",
.data = &printk_ratelimit_burst,
.data = &printk_ratelimit_state.burst,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
Expand Down
55 changes: 30 additions & 25 deletions lib/ratelimit.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
*
* 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.
*
*/
Expand All @@ -11,41 +14,43 @@
#include <linux/jiffies.h>
#include <linux/module.h>

static DEFINE_SPINLOCK(ratelimit_lock);
static unsigned long flags;

/*
* __ratelimit - rate limiting
* @ratelimit_jiffies: minimum time in jiffies between two callbacks
* @ratelimit_burst: number of callbacks we do before ratelimiting
* @rs: ratelimit_state data
*
* This enforces a rate limit: not more than @ratelimit_burst callbacks
* in every ratelimit_jiffies
* This enforces a rate limit: not more than @rs->ratelimit_burst callbacks
* in every @rs->ratelimit_jiffies
*/
int __ratelimit(int ratelimit_jiffies, int ratelimit_burst)
int __ratelimit(struct ratelimit_state *rs)
{
static DEFINE_SPINLOCK(ratelimit_lock);
static unsigned toks = 10 * 5 * HZ;
static unsigned long last_msg;
static int missed;
unsigned long flags;
unsigned long now = jiffies;
if (!rs->interval)
return 1;

spin_lock_irqsave(&ratelimit_lock, flags);
toks += now - last_msg;
last_msg = now;
if (toks > (ratelimit_burst * ratelimit_jiffies))
toks = ratelimit_burst * ratelimit_jiffies;
if (toks >= ratelimit_jiffies) {
int lost = missed;
if (!rs->begin)
rs->begin = jiffies;

missed = 0;
toks -= ratelimit_jiffies;
spin_unlock_irqrestore(&ratelimit_lock, flags);
if (lost)
printk(KERN_WARNING "%s: %d messages suppressed\n",
__func__, lost);
return 1;
if (time_is_before_jiffies(rs->begin + rs->interval)) {
if (rs->missed)
printk(KERN_WARNING "%s: %d callbacks suppressed\n",
__func__, rs->missed);
rs->begin = 0;
rs->printed = 0;
rs->missed = 0;
}
missed++;
if (rs->burst && rs->burst > rs->printed)
goto print;

rs->missed++;
spin_unlock_irqrestore(&ratelimit_lock, flags);
return 0;

print:
rs->printed++;
spin_unlock_irqrestore(&ratelimit_lock, flags);
return 1;
}
EXPORT_SYMBOL(__ratelimit);
4 changes: 2 additions & 2 deletions net/core/sysctl_net_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ static struct ctl_table net_core_table[] = {
{
.ctl_name = NET_CORE_MSG_COST,
.procname = "message_cost",
.data = &net_msg_cost,
.data = &net_ratelimit_state.interval,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
Expand All @@ -76,7 +76,7 @@ static struct ctl_table net_core_table[] = {
{
.ctl_name = NET_CORE_MSG_BURST,
.procname = "message_burst",
.data = &net_msg_burst,
.data = &net_ratelimit_state.burst,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
Expand Down
5 changes: 2 additions & 3 deletions net/core/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,16 @@
#include <asm/system.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;
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.
*/
int net_ratelimit(void)
{
return __printk_ratelimit(net_msg_cost, net_msg_burst);
return __ratelimit(&net_ratelimit_state);
}
EXPORT_SYMBOL(net_ratelimit);

Expand Down

0 comments on commit 717115e

Please sign in to comment.