rcu: Avoid waking up CPUs having only kfree_rcu() callbacks
When CONFIG_RCU_FAST_NO_HZ is enabled, RCU will allow a given CPU to enter dyntick-idle mode even if it still has RCU callbacks queued. RCU avoids system hangs in this case by scheduling a timer for several jiffies in the future. However, if all of the callbacks on that CPU are from kfree_rcu(), there is no reason to wake the CPU up, as it is not a problem to defer freeing of memory. This commit therefore tracks the number of callbacks on a given CPU that are from kfree_rcu(), and avoids scheduling the timer if all of a given CPU's callbacks are from kfree_rcu(). Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
This commit is contained in:
committed by
Paul E. McKenney
parent
0bb7b59d6e
commit
486e259340
@@ -671,10 +671,24 @@ static void rcu_preempt_do_callbacks(void)
|
||||
*/
|
||||
void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_preempt_state);
|
||||
__call_rcu(head, func, &rcu_preempt_state, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
/*
|
||||
* Queue an RCU callback for lazy invocation after a grace period.
|
||||
* This will likely be later named something like "call_rcu_lazy()",
|
||||
* but this change will require some way of tagging the lazy RCU
|
||||
* callbacks in the list of pending callbacks. Until then, this
|
||||
* function may only be called from __kfree_rcu().
|
||||
*/
|
||||
void kfree_call_rcu(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_preempt_state, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kfree_call_rcu);
|
||||
|
||||
/**
|
||||
* synchronize_rcu - wait until a grace period has elapsed.
|
||||
*
|
||||
@@ -1064,6 +1078,22 @@ static void rcu_preempt_process_callbacks(void)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue an RCU callback for lazy invocation after a grace period.
|
||||
* This will likely be later named something like "call_rcu_lazy()",
|
||||
* but this change will require some way of tagging the lazy RCU
|
||||
* callbacks in the list of pending callbacks. Until then, this
|
||||
* function may only be called from __kfree_rcu().
|
||||
*
|
||||
* Because there is no preemptible RCU, we use RCU-sched instead.
|
||||
*/
|
||||
void kfree_call_rcu(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *rcu))
|
||||
{
|
||||
__call_rcu(head, func, &rcu_sched_state, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kfree_call_rcu);
|
||||
|
||||
/*
|
||||
* Wait for an rcu-preempt grace period, but make it happen quickly.
|
||||
* But because preemptible RCU does not exist, map to rcu-sched.
|
||||
@@ -2051,6 +2081,48 @@ int rcu_needs_cpu(int cpu)
|
||||
return per_cpu(rcu_dyntick_holdoff, cpu) == jiffies;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does the specified flavor of RCU have non-lazy callbacks pending on
|
||||
* the specified CPU? Both RCU flavor and CPU are specified by the
|
||||
* rcu_data structure.
|
||||
*/
|
||||
static bool __rcu_cpu_has_nonlazy_callbacks(struct rcu_data *rdp)
|
||||
{
|
||||
return rdp->qlen != rdp->qlen_lazy;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TREE_PREEMPT_RCU
|
||||
|
||||
/*
|
||||
* Are there non-lazy RCU-preempt callbacks? (There cannot be if there
|
||||
* is no RCU-preempt in the kernel.)
|
||||
*/
|
||||
static bool rcu_preempt_cpu_has_nonlazy_callbacks(int cpu)
|
||||
{
|
||||
struct rcu_data *rdp = &per_cpu(rcu_preempt_data, cpu);
|
||||
|
||||
return __rcu_cpu_has_nonlazy_callbacks(rdp);
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
|
||||
static bool rcu_preempt_cpu_has_nonlazy_callbacks(int cpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* else #ifdef CONFIG_TREE_PREEMPT_RCU */
|
||||
|
||||
/*
|
||||
* Does any flavor of RCU have non-lazy callbacks on the specified CPU?
|
||||
*/
|
||||
static bool rcu_cpu_has_nonlazy_callbacks(int cpu)
|
||||
{
|
||||
return __rcu_cpu_has_nonlazy_callbacks(&per_cpu(rcu_sched_data, cpu)) ||
|
||||
__rcu_cpu_has_nonlazy_callbacks(&per_cpu(rcu_bh_data, cpu)) ||
|
||||
rcu_preempt_cpu_has_nonlazy_callbacks(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Timer handler used to force CPU to start pushing its remaining RCU
|
||||
* callbacks in the case where it entered dyntick-idle mode with callbacks
|
||||
@@ -2149,8 +2221,9 @@ static void rcu_prepare_for_idle(int cpu)
|
||||
trace_rcu_prep_idle("Dyntick with callbacks");
|
||||
per_cpu(rcu_dyntick_drain, cpu) = 0;
|
||||
per_cpu(rcu_dyntick_holdoff, cpu) = jiffies - 1;
|
||||
hrtimer_start(&per_cpu(rcu_idle_gp_timer, cpu),
|
||||
rcu_idle_gp_wait, HRTIMER_MODE_REL);
|
||||
if (rcu_cpu_has_nonlazy_callbacks(cpu))
|
||||
hrtimer_start(&per_cpu(rcu_idle_gp_timer, cpu),
|
||||
rcu_idle_gp_wait, HRTIMER_MODE_REL);
|
||||
return; /* Nothing more to do immediately. */
|
||||
} else if (--per_cpu(rcu_dyntick_drain, cpu) <= 0) {
|
||||
/* We have hit the limit, so time to give up. */
|
||||
|
Reference in New Issue
Block a user