On Mon, Aug 11, 2014 at 6:49 PM, Paul E. McKenney <paul...@linux.vnet.ibm.com> wrote: > From: "Paul E. McKenney" <paul...@linux.vnet.ibm.com> > > It is expected that many sites will have CONFIG_TASKS_RCU=y, but > will never actually invoke call_rcu_tasks(). For such sites, creating > rcu_tasks_kthread() at boot is wasteful. This commit therefore defers > creation of this kthread until the time of the first call_rcu_tasks(). > > This of course means that the first call_rcu_tasks() must be invoked > from process context after the scheduler is fully operational. > > Signed-off-by: Paul E. McKenney <paul...@linux.vnet.ibm.com> > --- > kernel/rcu/update.c | 33 ++++++++++++++++++++++++++------- > 1 file changed, 26 insertions(+), 7 deletions(-) > > diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c > index 1256a900cd01..d997163c7e92 100644 > --- a/kernel/rcu/update.c > +++ b/kernel/rcu/update.c > @@ -378,7 +378,12 @@ DEFINE_SRCU(tasks_rcu_exit_srcu); > static int rcu_task_stall_timeout __read_mostly = HZ * 60 * 10; > module_param(rcu_task_stall_timeout, int, 0644); > > -/* Post an RCU-tasks callback. */ > +static void rcu_spawn_tasks_kthread(void); > + > +/* > + * Post an RCU-tasks callback. First call must be from process context > + * after the scheduler if fully operational. > + */ > void call_rcu_tasks(struct rcu_head *rhp, void (*func)(struct rcu_head *rhp)) > { > unsigned long flags; > @@ -391,8 +396,10 @@ void call_rcu_tasks(struct rcu_head *rhp, void > (*func)(struct rcu_head *rhp)) > *rcu_tasks_cbs_tail = rhp; > rcu_tasks_cbs_tail = &rhp->next; > raw_spin_unlock_irqrestore(&rcu_tasks_cbs_lock, flags); > - if (needwake) > + if (needwake) { > + rcu_spawn_tasks_kthread(); > wake_up(&rcu_tasks_cbs_wq); > + } > } > EXPORT_SYMBOL_GPL(call_rcu_tasks); > > @@ -618,15 +625,27 @@ static int __noreturn rcu_tasks_kthread(void *arg) > } > } > > -/* Spawn rcu_tasks_kthread() at boot time. */ > -static int __init rcu_spawn_tasks_kthread(void) > +/* Spawn rcu_tasks_kthread() at first call to call_rcu_tasks(). */ > +static void rcu_spawn_tasks_kthread(void) > { > - struct task_struct __maybe_unused *t; > + static DEFINE_MUTEX(rcu_tasks_kthread_mutex); > + static struct task_struct *rcu_tasks_kthread_ptr; > + struct task_struct *t; > > + if (ACCESS_ONCE(rcu_tasks_kthread_ptr)) { > + smp_mb(); /* Ensure caller sees full kthread. */ > + return; > + }
I don't see the need for this smp_mb(). The caller has already seen that rcu_tasks_kthread_ptr is assigned. What are we ensuring with this barrier again? an smp_rmb() before this ACCESS_ONCE() and an smp_wmb() after assigning to rcu_tasks_kthread_ptr should be enough, right? > + mutex_lock(&rcu_tasks_kthread_mutex); > + if (rcu_tasks_kthread_ptr) { > + mutex_unlock(&rcu_tasks_kthread_mutex); > + return; > + } > t = kthread_run(rcu_tasks_kthread, NULL, "rcu_tasks_kthread"); > BUG_ON(IS_ERR(t)); > - return 0; > + smp_mb(); /* Ensure others see full kthread. */ > + ACCESS_ONCE(rcu_tasks_kthread_ptr) = t; Isn't it better to reverse these two statements and change as follows? ACCESS_ONCE(rcu_tasks_kthread_ptr) = t; smp_wmb(); or smp_store_release(rcu_tasks_kthread_ptr, t); will ensure that this write to rcu_task_kthread_ptr is ordered with the previous read. I recently read memory-barriers.txt, so please excuse me if I am totally wrong. But I am confused! :( > + mutex_unlock(&rcu_tasks_kthread_mutex); > } > -early_initcall(rcu_spawn_tasks_kthread); > > #endif /* #ifdef CONFIG_TASKS_RCU */ > -- > 1.8.1.5 > -- Pranith -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/