Commit b3dbec76 authored by Paul E. McKenney's avatar Paul E. McKenney

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: default avatarMike Galbraith <mgalbraith@suse.de>
Reported-by: default avatarDimitri Sivanich <sivanich@sgi.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: default avatarJosh Triplett <josh@joshtriplett.org>
parent a10d206e
...@@ -1042,49 +1042,52 @@ rcu_start_gp_per_cpu(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_dat ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 { ...@@ -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. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment