diff options
author | Dave Young <hidave.darkstar@gmail.com> | 2008-07-25 04:45:58 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-25 13:53:29 -0400 |
commit | 717115e1a5856b57af0f71e1df7149108294fc10 (patch) | |
tree | 9528a992245c2fb993a0cf0bc8221dc7dea5d259 | |
parent | 2711b793eb62a5873a0ba583a69252040aef176e (diff) |
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: 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>
-rw-r--r-- | include/asm-generic/bug.h | 3 | ||||
-rw-r--r-- | include/linux/kernel.h | 8 | ||||
-rw-r--r-- | include/linux/net.h | 3 | ||||
-rw-r--r-- | include/linux/ratelimit.h | 27 | ||||
-rw-r--r-- | include/linux/rcupreempt.h | 9 | ||||
-rw-r--r-- | kernel/printk.c | 17 | ||||
-rw-r--r-- | kernel/sysctl.c | 4 | ||||
-rw-r--r-- | lib/ratelimit.c | 55 | ||||
-rw-r--r-- | net/core/sysctl_net_core.c | 4 | ||||
-rw-r--r-- | net/core/utils.c | 5 |
10 files changed, 79 insertions, 56 deletions
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h index a346e744e770..a3f738cffdb6 100644 --- a/include/asm-generic/bug.h +++ b/include/asm-generic/bug.h | |||
@@ -97,6 +97,9 @@ extern void warn_slowpath(const char *file, const int line, | |||
97 | unlikely(__ret_warn_once); \ | 97 | unlikely(__ret_warn_once); \ |
98 | }) | 98 | }) |
99 | 99 | ||
100 | #define WARN_ON_RATELIMIT(condition, state) \ | ||
101 | WARN_ON((condition) && __ratelimit(state)) | ||
102 | |||
100 | #ifdef CONFIG_SMP | 103 | #ifdef CONFIG_SMP |
101 | # define WARN_ON_SMP(x) WARN_ON(x) | 104 | # define WARN_ON_SMP(x) WARN_ON(x) |
102 | #else | 105 | #else |
diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 5c4b1251e110..fdbbf72ca2eb 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/bitops.h> | 15 | #include <linux/bitops.h> |
16 | #include <linux/log2.h> | 16 | #include <linux/log2.h> |
17 | #include <linux/typecheck.h> | 17 | #include <linux/typecheck.h> |
18 | #include <linux/ratelimit.h> | ||
18 | #include <asm/byteorder.h> | 19 | #include <asm/byteorder.h> |
19 | #include <asm/bug.h> | 20 | #include <asm/bug.h> |
20 | 21 | ||
@@ -189,11 +190,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) | |||
189 | asmlinkage int printk(const char * fmt, ...) | 190 | asmlinkage int printk(const char * fmt, ...) |
190 | __attribute__ ((format (printf, 1, 2))) __cold; | 191 | __attribute__ ((format (printf, 1, 2))) __cold; |
191 | 192 | ||
192 | extern int printk_ratelimit_jiffies; | 193 | extern struct ratelimit_state printk_ratelimit_state; |
193 | extern int printk_ratelimit_burst; | ||
194 | extern int printk_ratelimit(void); | 194 | extern int printk_ratelimit(void); |
195 | extern int __ratelimit(int ratelimit_jiffies, int ratelimit_burst); | ||
196 | extern int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst); | ||
197 | extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, | 195 | extern bool printk_timed_ratelimit(unsigned long *caller_jiffies, |
198 | unsigned int interval_msec); | 196 | unsigned int interval_msec); |
199 | #else | 197 | #else |
@@ -204,8 +202,6 @@ static inline int printk(const char *s, ...) | |||
204 | __attribute__ ((format (printf, 1, 2))); | 202 | __attribute__ ((format (printf, 1, 2))); |
205 | static inline int __cold printk(const char *s, ...) { return 0; } | 203 | static inline int __cold printk(const char *s, ...) { return 0; } |
206 | static inline int printk_ratelimit(void) { return 0; } | 204 | static inline int printk_ratelimit(void) { return 0; } |
207 | static inline int __printk_ratelimit(int ratelimit_jiffies, \ | ||
208 | int ratelimit_burst) { return 0; } | ||
209 | static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \ | 205 | static inline bool printk_timed_ratelimit(unsigned long *caller_jiffies, \ |
210 | unsigned int interval_msec) \ | 206 | unsigned int interval_msec) \ |
211 | { return false; } | 207 | { return false; } |
diff --git a/include/linux/net.h b/include/linux/net.h index 2f999fbb188d..4a9a30f2d68f 100644 --- a/include/linux/net.h +++ b/include/linux/net.h | |||
@@ -351,8 +351,7 @@ static const struct proto_ops name##_ops = { \ | |||
351 | 351 | ||
352 | #ifdef CONFIG_SYSCTL | 352 | #ifdef CONFIG_SYSCTL |
353 | #include <linux/sysctl.h> | 353 | #include <linux/sysctl.h> |
354 | extern int net_msg_cost; | 354 | extern struct ratelimit_state net_ratelimit_state; |
355 | extern int net_msg_burst; | ||
356 | #endif | 355 | #endif |
357 | 356 | ||
358 | #endif /* __KERNEL__ */ | 357 | #endif /* __KERNEL__ */ |
diff --git a/include/linux/ratelimit.h b/include/linux/ratelimit.h new file mode 100644 index 000000000000..18a5b9ba9d40 --- /dev/null +++ b/include/linux/ratelimit.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef _LINUX_RATELIMIT_H | ||
2 | #define _LINUX_RATELIMIT_H | ||
3 | #include <linux/param.h> | ||
4 | |||
5 | #define DEFAULT_RATELIMIT_INTERVAL (5 * HZ) | ||
6 | #define DEFAULT_RATELIMIT_BURST 10 | ||
7 | |||
8 | struct ratelimit_state { | ||
9 | int interval; | ||
10 | int burst; | ||
11 | int printed; | ||
12 | int missed; | ||
13 | unsigned long begin; | ||
14 | }; | ||
15 | |||
16 | #define DEFINE_RATELIMIT_STATE(name, interval, burst) \ | ||
17 | struct ratelimit_state name = {interval, burst,} | ||
18 | |||
19 | extern int __ratelimit(struct ratelimit_state *rs); | ||
20 | |||
21 | static inline int ratelimit(void) | ||
22 | { | ||
23 | static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, | ||
24 | DEFAULT_RATELIMIT_BURST); | ||
25 | return __ratelimit(&rs); | ||
26 | } | ||
27 | #endif | ||
diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h index f04b64eca636..0967f03b0705 100644 --- a/include/linux/rcupreempt.h +++ b/include/linux/rcupreempt.h | |||
@@ -115,16 +115,21 @@ DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched); | |||
115 | 115 | ||
116 | static inline void rcu_enter_nohz(void) | 116 | static inline void rcu_enter_nohz(void) |
117 | { | 117 | { |
118 | static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1); | ||
119 | |||
118 | smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ | 120 | smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */ |
119 | __get_cpu_var(rcu_dyntick_sched).dynticks++; | 121 | __get_cpu_var(rcu_dyntick_sched).dynticks++; |
120 | WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1); | 122 | WARN_ON_RATELIMIT(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1, &rs); |
121 | } | 123 | } |
122 | 124 | ||
123 | static inline void rcu_exit_nohz(void) | 125 | static inline void rcu_exit_nohz(void) |
124 | { | 126 | { |
127 | static DEFINE_RATELIMIT_STATE(rs, 10 * HZ, 1); | ||
128 | |||
125 | smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ | 129 | smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */ |
126 | __get_cpu_var(rcu_dyntick_sched).dynticks++; | 130 | __get_cpu_var(rcu_dyntick_sched).dynticks++; |
127 | WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1)); | 131 | WARN_ON_RATELIMIT(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1), |
132 | &rs); | ||
128 | } | 133 | } |
129 | 134 | ||
130 | #else /* CONFIG_NO_HZ */ | 135 | #else /* CONFIG_NO_HZ */ |
diff --git a/kernel/printk.c b/kernel/printk.c index 3f7a2a94583b..a7f7559c5f6c 100644 --- a/kernel/printk.c +++ b/kernel/printk.c | |||
@@ -1308,6 +1308,8 @@ void tty_write_message(struct tty_struct *tty, char *msg) | |||
1308 | } | 1308 | } |
1309 | 1309 | ||
1310 | #if defined CONFIG_PRINTK | 1310 | #if defined CONFIG_PRINTK |
1311 | |||
1312 | DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10); | ||
1311 | /* | 1313 | /* |
1312 | * printk rate limiting, lifted from the networking subsystem. | 1314 | * printk rate limiting, lifted from the networking subsystem. |
1313 | * | 1315 | * |
@@ -1315,22 +1317,9 @@ void tty_write_message(struct tty_struct *tty, char *msg) | |||
1315 | * every printk_ratelimit_jiffies to make a denial-of-service | 1317 | * every printk_ratelimit_jiffies to make a denial-of-service |
1316 | * attack impossible. | 1318 | * attack impossible. |
1317 | */ | 1319 | */ |
1318 | int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst) | ||
1319 | { | ||
1320 | return __ratelimit(ratelimit_jiffies, ratelimit_burst); | ||
1321 | } | ||
1322 | EXPORT_SYMBOL(__printk_ratelimit); | ||
1323 | |||
1324 | /* minimum time in jiffies between messages */ | ||
1325 | int printk_ratelimit_jiffies = 5 * HZ; | ||
1326 | |||
1327 | /* number of messages we send before ratelimiting */ | ||
1328 | int printk_ratelimit_burst = 10; | ||
1329 | |||
1330 | int printk_ratelimit(void) | 1320 | int printk_ratelimit(void) |
1331 | { | 1321 | { |
1332 | return __printk_ratelimit(printk_ratelimit_jiffies, | 1322 | return __ratelimit(&printk_ratelimit_state); |
1333 | printk_ratelimit_burst); | ||
1334 | } | 1323 | } |
1335 | EXPORT_SYMBOL(printk_ratelimit); | 1324 | EXPORT_SYMBOL(printk_ratelimit); |
1336 | 1325 | ||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1a8299d1fe59..35a50db9b6ce 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -624,7 +624,7 @@ static struct ctl_table kern_table[] = { | |||
624 | { | 624 | { |
625 | .ctl_name = KERN_PRINTK_RATELIMIT, | 625 | .ctl_name = KERN_PRINTK_RATELIMIT, |
626 | .procname = "printk_ratelimit", | 626 | .procname = "printk_ratelimit", |
627 | .data = &printk_ratelimit_jiffies, | 627 | .data = &printk_ratelimit_state.interval, |
628 | .maxlen = sizeof(int), | 628 | .maxlen = sizeof(int), |
629 | .mode = 0644, | 629 | .mode = 0644, |
630 | .proc_handler = &proc_dointvec_jiffies, | 630 | .proc_handler = &proc_dointvec_jiffies, |
@@ -633,7 +633,7 @@ static struct ctl_table kern_table[] = { | |||
633 | { | 633 | { |
634 | .ctl_name = KERN_PRINTK_RATELIMIT_BURST, | 634 | .ctl_name = KERN_PRINTK_RATELIMIT_BURST, |
635 | .procname = "printk_ratelimit_burst", | 635 | .procname = "printk_ratelimit_burst", |
636 | .data = &printk_ratelimit_burst, | 636 | .data = &printk_ratelimit_state.burst, |
637 | .maxlen = sizeof(int), | 637 | .maxlen = sizeof(int), |
638 | .mode = 0644, | 638 | .mode = 0644, |
639 | .proc_handler = &proc_dointvec, | 639 | .proc_handler = &proc_dointvec, |
diff --git a/lib/ratelimit.c b/lib/ratelimit.c index 485e3040dcd4..35136671b215 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c | |||
@@ -3,6 +3,9 @@ | |||
3 | * | 3 | * |
4 | * Isolated from kernel/printk.c by Dave Young <hidave.darkstar@gmail.com> | 4 | * Isolated from kernel/printk.c by Dave Young <hidave.darkstar@gmail.com> |
5 | * | 5 | * |
6 | * 2008-05-01 rewrite the function and use a ratelimit_state data struct as | ||
7 | * parameter. Now every user can use their own standalone ratelimit_state. | ||
8 | * | ||
6 | * This file is released under the GPLv2. | 9 | * This file is released under the GPLv2. |
7 | * | 10 | * |
8 | */ | 11 | */ |
@@ -11,41 +14,43 @@ | |||
11 | #include <linux/jiffies.h> | 14 | #include <linux/jiffies.h> |
12 | #include <linux/module.h> | 15 | #include <linux/module.h> |
13 | 16 | ||
17 | static DEFINE_SPINLOCK(ratelimit_lock); | ||
18 | static unsigned long flags; | ||
19 | |||
14 | /* | 20 | /* |
15 | * __ratelimit - rate limiting | 21 | * __ratelimit - rate limiting |
16 | * @ratelimit_jiffies: minimum time in jiffies between two callbacks | 22 | * @rs: ratelimit_state data |
17 | * @ratelimit_burst: number of callbacks we do before ratelimiting | ||
18 | * | 23 | * |
19 | * This enforces a rate limit: not more than @ratelimit_burst callbacks | 24 | * This enforces a rate limit: not more than @rs->ratelimit_burst callbacks |
20 | * in every ratelimit_jiffies | 25 | * in every @rs->ratelimit_jiffies |
21 | */ | 26 | */ |
22 | int __ratelimit(int ratelimit_jiffies, int ratelimit_burst) | 27 | int __ratelimit(struct ratelimit_state *rs) |
23 | { | 28 | { |
24 | static DEFINE_SPINLOCK(ratelimit_lock); | 29 | if (!rs->interval) |
25 | static unsigned toks = 10 * 5 * HZ; | 30 | return 1; |
26 | static unsigned long last_msg; | ||
27 | static int missed; | ||
28 | unsigned long flags; | ||
29 | unsigned long now = jiffies; | ||
30 | 31 | ||
31 | spin_lock_irqsave(&ratelimit_lock, flags); | 32 | spin_lock_irqsave(&ratelimit_lock, flags); |
32 | toks += now - last_msg; | 33 | if (!rs->begin) |
33 | last_msg = now; | 34 | rs->begin = jiffies; |
34 | if (toks > (ratelimit_burst * ratelimit_jiffies)) | ||
35 | toks = ratelimit_burst * ratelimit_jiffies; | ||
36 | if (toks >= ratelimit_jiffies) { | ||
37 | int lost = missed; | ||
38 | 35 | ||
39 | missed = 0; | 36 | if (time_is_before_jiffies(rs->begin + rs->interval)) { |
40 | toks -= ratelimit_jiffies; | 37 | if (rs->missed) |
41 | spin_unlock_irqrestore(&ratelimit_lock, flags); | 38 | printk(KERN_WARNING "%s: %d callbacks suppressed\n", |
42 | if (lost) | 39 | __func__, rs->missed); |
43 | printk(KERN_WARNING "%s: %d messages suppressed\n", | 40 | rs->begin = 0; |
44 | __func__, lost); | 41 | rs->printed = 0; |
45 | return 1; | 42 | rs->missed = 0; |
46 | } | 43 | } |
47 | missed++; | 44 | if (rs->burst && rs->burst > rs->printed) |
45 | goto print; | ||
46 | |||
47 | rs->missed++; | ||
48 | spin_unlock_irqrestore(&ratelimit_lock, flags); | 48 | spin_unlock_irqrestore(&ratelimit_lock, flags); |
49 | return 0; | 49 | return 0; |
50 | |||
51 | print: | ||
52 | rs->printed++; | ||
53 | spin_unlock_irqrestore(&ratelimit_lock, flags); | ||
54 | return 1; | ||
50 | } | 55 | } |
51 | EXPORT_SYMBOL(__ratelimit); | 56 | EXPORT_SYMBOL(__ratelimit); |
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index a570e2af22cb..f686467ff12b 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c | |||
@@ -67,7 +67,7 @@ static struct ctl_table net_core_table[] = { | |||
67 | { | 67 | { |
68 | .ctl_name = NET_CORE_MSG_COST, | 68 | .ctl_name = NET_CORE_MSG_COST, |
69 | .procname = "message_cost", | 69 | .procname = "message_cost", |
70 | .data = &net_msg_cost, | 70 | .data = &net_ratelimit_state.interval, |
71 | .maxlen = sizeof(int), | 71 | .maxlen = sizeof(int), |
72 | .mode = 0644, | 72 | .mode = 0644, |
73 | .proc_handler = &proc_dointvec_jiffies, | 73 | .proc_handler = &proc_dointvec_jiffies, |
@@ -76,7 +76,7 @@ static struct ctl_table net_core_table[] = { | |||
76 | { | 76 | { |
77 | .ctl_name = NET_CORE_MSG_BURST, | 77 | .ctl_name = NET_CORE_MSG_BURST, |
78 | .procname = "message_burst", | 78 | .procname = "message_burst", |
79 | .data = &net_msg_burst, | 79 | .data = &net_ratelimit_state.burst, |
80 | .maxlen = sizeof(int), | 80 | .maxlen = sizeof(int), |
81 | .mode = 0644, | 81 | .mode = 0644, |
82 | .proc_handler = &proc_dointvec, | 82 | .proc_handler = &proc_dointvec, |
diff --git a/net/core/utils.c b/net/core/utils.c index 8031eb59054e..72e0ebe964a0 100644 --- a/net/core/utils.c +++ b/net/core/utils.c | |||
@@ -31,17 +31,16 @@ | |||
31 | #include <asm/system.h> | 31 | #include <asm/system.h> |
32 | #include <asm/uaccess.h> | 32 | #include <asm/uaccess.h> |
33 | 33 | ||
34 | int net_msg_cost __read_mostly = 5*HZ; | ||
35 | int net_msg_burst __read_mostly = 10; | ||
36 | int net_msg_warn __read_mostly = 1; | 34 | int net_msg_warn __read_mostly = 1; |
37 | EXPORT_SYMBOL(net_msg_warn); | 35 | EXPORT_SYMBOL(net_msg_warn); |
38 | 36 | ||
37 | DEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10); | ||
39 | /* | 38 | /* |
40 | * All net warning printk()s should be guarded by this function. | 39 | * All net warning printk()s should be guarded by this function. |
41 | */ | 40 | */ |
42 | int net_ratelimit(void) | 41 | int net_ratelimit(void) |
43 | { | 42 | { |
44 | return __printk_ratelimit(net_msg_cost, net_msg_burst); | 43 | return __ratelimit(&net_ratelimit_state); |
45 | } | 44 | } |
46 | EXPORT_SYMBOL(net_ratelimit); | 45 | EXPORT_SYMBOL(net_ratelimit); |
47 | 46 | ||