sched,rcu: Make cond_resched() report RCU quiescent states
Given a CPU running a loop containing cond_resched(), with no other tasks runnable on that CPU, RCU will eventually report RCU CPU stall warnings due to lack of quiescent states. Fortunately, every call to cond_resched() is a perfectly good quiescent state. Unfortunately, invoking rcu_note_context_switch() is a bit heavyweight for cond_resched(), especially given the need to disable preemption, and, for RCU-preempt, interrupts as well. This commit therefore maintains a per-CPU counter that causes cond_resched(), cond_resched_lock(), and cond_resched_softirq() to call rcu_note_context_switch(), but only about once per 256 invocations. This ratio was chosen in keeping with the relative time constants of RCU grace periods. Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
This commit is contained in:
@@ -338,3 +338,21 @@ static int __init check_cpu_stall_init(void)
|
||||
early_initcall(check_cpu_stall_init);
|
||||
|
||||
#endif /* #ifdef CONFIG_RCU_STALL_COMMON */
|
||||
|
||||
/*
|
||||
* Hooks for cond_resched() and friends to avoid RCU CPU stall warnings.
|
||||
*/
|
||||
|
||||
DEFINE_PER_CPU(int, rcu_cond_resched_count);
|
||||
|
||||
/*
|
||||
* Report a set of RCU quiescent states, for use by cond_resched()
|
||||
* and friends. Out of line due to being called infrequently.
|
||||
*/
|
||||
void rcu_resched(void)
|
||||
{
|
||||
preempt_disable();
|
||||
__this_cpu_write(rcu_cond_resched_count, 0);
|
||||
rcu_note_context_switch(smp_processor_id());
|
||||
preempt_enable();
|
||||
}
|
||||
|
@@ -4051,6 +4051,7 @@ static void __cond_resched(void)
|
||||
|
||||
int __sched _cond_resched(void)
|
||||
{
|
||||
rcu_cond_resched();
|
||||
if (should_resched()) {
|
||||
__cond_resched();
|
||||
return 1;
|
||||
@@ -4069,15 +4070,18 @@ EXPORT_SYMBOL(_cond_resched);
|
||||
*/
|
||||
int __cond_resched_lock(spinlock_t *lock)
|
||||
{
|
||||
bool need_rcu_resched = rcu_should_resched();
|
||||
int resched = should_resched();
|
||||
int ret = 0;
|
||||
|
||||
lockdep_assert_held(lock);
|
||||
|
||||
if (spin_needbreak(lock) || resched) {
|
||||
if (spin_needbreak(lock) || resched || need_rcu_resched) {
|
||||
spin_unlock(lock);
|
||||
if (resched)
|
||||
__cond_resched();
|
||||
else if (unlikely(need_rcu_resched))
|
||||
rcu_resched();
|
||||
else
|
||||
cpu_relax();
|
||||
ret = 1;
|
||||
@@ -4091,6 +4095,7 @@ int __sched __cond_resched_softirq(void)
|
||||
{
|
||||
BUG_ON(!in_softirq());
|
||||
|
||||
rcu_cond_resched(); /* BH disabled OK, just recording QSes. */
|
||||
if (should_resched()) {
|
||||
local_bh_enable();
|
||||
__cond_resched();
|
||||
|
Reference in New Issue
Block a user