diff options
Diffstat (limited to 'lib/ratelimit.c')
-rw-r--r-- | lib/ratelimit.c | 51 |
1 files changed, 38 insertions, 13 deletions
diff --git a/lib/ratelimit.c b/lib/ratelimit.c index bd6e3b429e33..90c9fe57eb42 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c @@ -39,12 +39,22 @@ int ___ratelimit(struct ratelimit_state *rs, const char *func) return 1; /* - * If we contend on this state's lock then almost - * by definition we are too busy to print a message, - * in addition to the one that will be printed by - * the entity that is holding the lock already: + * If we contend on this state's lock then just check if + * the current burst is used or not. It might cause + * false positive when we are past the interval and + * the current lock owner is just about to reset it. */ if (!raw_spin_trylock_irqsave(&rs->lock, flags)) { + unsigned int rs_flags = READ_ONCE(rs->flags); + + if (rs_flags & RATELIMIT_INITIALIZED && burst) { + int n_left; + + n_left = atomic_dec_return(&rs->rs_n_left); + if (n_left >= 0) + return 1; + } + ratelimit_state_inc_miss(rs); return 0; } @@ -52,27 +62,42 @@ int ___ratelimit(struct ratelimit_state *rs, const char *func) if (!(rs->flags & RATELIMIT_INITIALIZED)) { rs->begin = jiffies; rs->flags |= RATELIMIT_INITIALIZED; + atomic_set(&rs->rs_n_left, rs->burst); } if (time_is_before_jiffies(rs->begin + interval)) { - int m = ratelimit_state_reset_miss(rs); + int m; + + /* + * Reset rs_n_left ASAP to reduce false positives + * in parallel calls, see above. + */ + atomic_set(&rs->rs_n_left, rs->burst); + rs->begin = jiffies; + m = ratelimit_state_reset_miss(rs); if (m) { if (!(rs->flags & RATELIMIT_MSG_ON_RELEASE)) { printk_deferred(KERN_WARNING "%s: %d callbacks suppressed\n", func, m); } } - rs->begin = jiffies; - rs->printed = 0; } - if (burst && burst > rs->printed) { - rs->printed++; - ret = 1; - } else { - ratelimit_state_inc_miss(rs); - ret = 0; + if (burst) { + int n_left; + + /* The burst might have been taken by a parallel call. */ + n_left = atomic_dec_return(&rs->rs_n_left); + if (n_left >= 0) { + ret = 1; + goto unlock_ret; + } } + + ratelimit_state_inc_miss(rs); + ret = 0; + +unlock_ret: raw_spin_unlock_irqrestore(&rs->lock, flags); return ret; |