rcu: Move RCU grace-period initialization into a kthread
As the first step towards allowing grace-period initialization to be preemptible, this commit moves the RCU grace-period initialization into its own kthread. This is needed to keep large-system scheduling latency at reasonable levels. Also change raw_spin_lock_irqsave() to raw_spin_lock_irq() as suggested by Peter Zijlstra in review comments. Reported-by: Mike Galbraith <mgalbraith@suse.de> Reported-by: Dimitri Sivanich <sivanich@sgi.com> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Reviewed-by: Josh Triplett <josh@joshtriplett.org>
This commit is contained in:
parent
a10d206ef1
commit
b3dbec76e5
2 changed files with 129 additions and 64 deletions
142
kernel/rcutree.c
142
kernel/rcutree.c
|
@ -1042,49 +1042,52 @@ rcu_start_gp_per_cpu(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_dat
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start a new RCU grace period if warranted, re-initializing the hierarchy
|
* Body of kthread that handles grace periods.
|
||||||
* in preparation for detecting the next grace period. The caller must hold
|
|
||||||
* the root node's ->lock, which is released before return. Hard irqs must
|
|
||||||
* be disabled.
|
|
||||||
*
|
|
||||||
* Note that it is legal for a dying CPU (which is marked as offline) to
|
|
||||||
* invoke this function. This can happen when the dying CPU reports its
|
|
||||||
* quiescent state.
|
|
||||||
*/
|
*/
|
||||||
static void
|
static int rcu_gp_kthread(void *arg)
|
||||||
rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
|
|
||||||
__releases(rcu_get_root(rsp)->lock)
|
|
||||||
{
|
{
|
||||||
struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
|
struct rcu_data *rdp;
|
||||||
struct rcu_node *rnp = rcu_get_root(rsp);
|
struct rcu_node *rnp;
|
||||||
|
struct rcu_state *rsp = arg;
|
||||||
|
|
||||||
if (!rcu_scheduler_fully_active ||
|
for (;;) {
|
||||||
!cpu_needs_another_gp(rsp, rdp)) {
|
|
||||||
|
/* Handle grace-period start. */
|
||||||
|
rnp = rcu_get_root(rsp);
|
||||||
|
for (;;) {
|
||||||
|
wait_event_interruptible(rsp->gp_wq, rsp->gp_flags);
|
||||||
|
if (rsp->gp_flags)
|
||||||
|
break;
|
||||||
|
flush_signals(current);
|
||||||
|
}
|
||||||
|
raw_spin_lock_irq(&rnp->lock);
|
||||||
|
rsp->gp_flags = 0;
|
||||||
|
rdp = this_cpu_ptr(rsp->rda);
|
||||||
|
|
||||||
|
if (rcu_gp_in_progress(rsp)) {
|
||||||
/*
|
/*
|
||||||
* Either the scheduler hasn't yet spawned the first
|
* A grace period is already in progress, so
|
||||||
* non-idle task or this CPU does not need another
|
* don't start another one.
|
||||||
* grace period. Either way, don't start a new grace
|
|
||||||
* period.
|
|
||||||
*/
|
*/
|
||||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
raw_spin_unlock_irq(&rnp->lock);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rsp->fqs_active) {
|
if (rsp->fqs_active) {
|
||||||
/*
|
/*
|
||||||
* This CPU needs a grace period, but force_quiescent_state()
|
* We need a grace period, but force_quiescent_state()
|
||||||
* is running. Tell it to start one on this CPU's behalf.
|
* is running. Tell it to start one on our behalf.
|
||||||
*/
|
*/
|
||||||
rsp->fqs_need_gp = 1;
|
rsp->fqs_need_gp = 1;
|
||||||
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
raw_spin_unlock_irq(&rnp->lock);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Advance to a new grace period and initialize state. */
|
/* Advance to a new grace period and initialize state. */
|
||||||
rsp->gpnum++;
|
rsp->gpnum++;
|
||||||
trace_rcu_grace_period(rsp->name, rsp->gpnum, "start");
|
trace_rcu_grace_period(rsp->name, rsp->gpnum, "start");
|
||||||
WARN_ON_ONCE(rsp->fqs_state == RCU_GP_INIT);
|
WARN_ON_ONCE(rsp->fqs_state == RCU_GP_INIT);
|
||||||
rsp->fqs_state = RCU_GP_INIT; /* Hold off force_quiescent_state. */
|
rsp->fqs_state = RCU_GP_INIT; /* Stop force_quiescent_state. */
|
||||||
rsp->jiffies_force_qs = jiffies + RCU_JIFFIES_TILL_FORCE_QS;
|
rsp->jiffies_force_qs = jiffies + RCU_JIFFIES_TILL_FORCE_QS;
|
||||||
record_gp_stall_check_time(rsp);
|
record_gp_stall_check_time(rsp);
|
||||||
raw_spin_unlock(&rnp->lock); /* leave irqs disabled. */
|
raw_spin_unlock(&rnp->lock); /* leave irqs disabled. */
|
||||||
|
@ -1095,19 +1098,19 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
|
||||||
/*
|
/*
|
||||||
* Set the quiescent-state-needed bits in all the rcu_node
|
* Set the quiescent-state-needed bits in all the rcu_node
|
||||||
* structures for all currently online CPUs in breadth-first
|
* structures for all currently online CPUs in breadth-first
|
||||||
* order, starting from the root rcu_node structure. This
|
* order, starting from the root rcu_node structure.
|
||||||
* operation relies on the layout of the hierarchy within the
|
* This operation relies on the layout of the hierarchy
|
||||||
* rsp->node[] array. Note that other CPUs will access only
|
* within the rsp->node[] array. Note that other CPUs will
|
||||||
* the leaves of the hierarchy, which still indicate that no
|
* access only the leaves of the hierarchy, which still
|
||||||
* grace period is in progress, at least until the corresponding
|
* indicate that no grace period is in progress, at least
|
||||||
* leaf node has been initialized. In addition, we have excluded
|
* until the corresponding leaf node has been initialized.
|
||||||
* CPU-hotplug operations.
|
* In addition, we have excluded CPU-hotplug operations.
|
||||||
*
|
*
|
||||||
* Note that the grace period cannot complete until we finish
|
* Note that the grace period cannot complete until
|
||||||
* the initialization process, as there will be at least one
|
* we finish the initialization process, as there will
|
||||||
* qsmask bit set in the root node until that time, namely the
|
* be at least one qsmask bit set in the root node until
|
||||||
* one corresponding to this CPU, due to the fact that we have
|
* that time, namely the one corresponding to this CPU,
|
||||||
* irqs disabled.
|
* due to the fact that we have irqs disabled.
|
||||||
*/
|
*/
|
||||||
rcu_for_each_node_breadth_first(rsp, rnp) {
|
rcu_for_each_node_breadth_first(rsp, rnp) {
|
||||||
raw_spin_lock(&rnp->lock); /* irqs already disabled. */
|
raw_spin_lock(&rnp->lock); /* irqs already disabled. */
|
||||||
|
@ -1126,9 +1129,45 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
|
||||||
|
|
||||||
rnp = rcu_get_root(rsp);
|
rnp = rcu_get_root(rsp);
|
||||||
raw_spin_lock(&rnp->lock); /* irqs already disabled. */
|
raw_spin_lock(&rnp->lock); /* irqs already disabled. */
|
||||||
rsp->fqs_state = RCU_SIGNAL_INIT; /* force_quiescent_state now OK. */
|
/* force_quiescent_state() now OK. */
|
||||||
|
rsp->fqs_state = RCU_SIGNAL_INIT;
|
||||||
raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
|
raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
|
||||||
raw_spin_unlock_irqrestore(&rsp->onofflock, flags);
|
raw_spin_unlock_irq(&rsp->onofflock);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start a new RCU grace period if warranted, re-initializing the hierarchy
|
||||||
|
* in preparation for detecting the next grace period. The caller must hold
|
||||||
|
* the root node's ->lock, which is released before return. Hard irqs must
|
||||||
|
* be disabled.
|
||||||
|
*
|
||||||
|
* Note that it is legal for a dying CPU (which is marked as offline) to
|
||||||
|
* invoke this function. This can happen when the dying CPU reports its
|
||||||
|
* quiescent state.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
|
||||||
|
__releases(rcu_get_root(rsp)->lock)
|
||||||
|
{
|
||||||
|
struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
|
||||||
|
struct rcu_node *rnp = rcu_get_root(rsp);
|
||||||
|
|
||||||
|
if (!rsp->gp_kthread ||
|
||||||
|
!cpu_needs_another_gp(rsp, rdp)) {
|
||||||
|
/*
|
||||||
|
* Either we have not yet spawned the grace-period
|
||||||
|
* task or this CPU does not need another grace period.
|
||||||
|
* Either way, don't start a new grace period.
|
||||||
|
*/
|
||||||
|
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp->gp_flags = 1;
|
||||||
|
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||||
|
wake_up(&rsp->gp_wq);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2628,6 +2667,28 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
|
||||||
return NOTIFY_OK;
|
return NOTIFY_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Spawn the kthread that handles this RCU flavor's grace periods.
|
||||||
|
*/
|
||||||
|
static int __init rcu_spawn_gp_kthread(void)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
struct rcu_node *rnp;
|
||||||
|
struct rcu_state *rsp;
|
||||||
|
struct task_struct *t;
|
||||||
|
|
||||||
|
for_each_rcu_flavor(rsp) {
|
||||||
|
t = kthread_run(rcu_gp_kthread, rsp, rsp->name);
|
||||||
|
BUG_ON(IS_ERR(t));
|
||||||
|
rnp = rcu_get_root(rsp);
|
||||||
|
raw_spin_lock_irqsave(&rnp->lock, flags);
|
||||||
|
rsp->gp_kthread = t;
|
||||||
|
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
early_initcall(rcu_spawn_gp_kthread);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function is invoked towards the end of the scheduler's initialization
|
* This function is invoked towards the end of the scheduler's initialization
|
||||||
* process. Before this is called, the idle task might contain
|
* process. Before this is called, the idle task might contain
|
||||||
|
@ -2729,6 +2790,7 @@ static void __init rcu_init_one(struct rcu_state *rsp,
|
||||||
}
|
}
|
||||||
|
|
||||||
rsp->rda = rda;
|
rsp->rda = rda;
|
||||||
|
init_waitqueue_head(&rsp->gp_wq);
|
||||||
rnp = rsp->level[rcu_num_lvls - 1];
|
rnp = rsp->level[rcu_num_lvls - 1];
|
||||||
for_each_possible_cpu(i) {
|
for_each_possible_cpu(i) {
|
||||||
while (i > rnp->grphi)
|
while (i > rnp->grphi)
|
||||||
|
|
|
@ -385,6 +385,9 @@ struct rcu_state {
|
||||||
u8 boost; /* Subject to priority boost. */
|
u8 boost; /* Subject to priority boost. */
|
||||||
unsigned long gpnum; /* Current gp number. */
|
unsigned long gpnum; /* Current gp number. */
|
||||||
unsigned long completed; /* # of last completed gp. */
|
unsigned long completed; /* # of last completed gp. */
|
||||||
|
struct task_struct *gp_kthread; /* Task for grace periods. */
|
||||||
|
wait_queue_head_t gp_wq; /* Where GP task waits. */
|
||||||
|
int gp_flags; /* Commands for GP task. */
|
||||||
|
|
||||||
/* End of fields guarded by root rcu_node's lock. */
|
/* End of fields guarded by root rcu_node's lock. */
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue