From: Suresh Siddha <[email protected]>
MTRR rendezvous sequence using stop_one_cpu_nowait() can potentially
happen in parallel with another system wide rendezvous using
stop_machine(). This can lead to deadlock (The order in which
works are queued can be different on different cpu's. Some cpu's
will be running the first rendezvous handler and others will be running
the second rendezvous handler. Each set waiting for the other set to join
for the system wide rendezvous, leading to a deadlock).
MTRR rendezvous sequence is not implemented using stop_machine() as this
gets called both from the process context aswell as the cpu online paths
(where the cpu has not come online and the interrupts are disabled etc).
stop_machine() works with only online cpus.
For now, take the stop_machine mutex in the MTRR rendezvous sequence that
gets called from an online cpu (here we are in the process context
and can potentially sleep while taking the mutex). And the MTRR rendezvous
that gets triggered during cpu online doesn't need to take this stop_machine
lock (as the stop_machine() already ensures that there is no cpu hotplug
going on in parallel by doing get_online_cpus())
TBD: Pursue a cleaner solution of extending the stop_machine()
infrastructure to handle the case where the calling cpu is
still not online and use this for MTRR rendezvous sequence.
fixes: https://bugzilla.novell.com/show_bug.cgi?id=672008
(will be forwarded to stable series for inclusion in kernels v2.6.35-v2.6.39
after some testing in mainline).
Reported-by: Vadim Kotelnikov <[email protected]>
Signed-off-by: Suresh Siddha <[email protected]>
Cc: [email protected] # 2.6.35+, backport a week or two after this gets more testing in mainline
---
arch/x86/kernel/cpu/mtrr/main.c | 16 ++++++++++++++++
include/linux/stop_machine.h | 2 ++
kernel/stop_machine.c | 2 +-
3 files changed, 19 insertions(+), 1 deletion(-)
Index: linux-2.6-tip/arch/x86/kernel/cpu/mtrr/main.c
===================================================================
--- linux-2.6-tip.orig/arch/x86/kernel/cpu/mtrr/main.c
+++ linux-2.6-tip/arch/x86/kernel/cpu/mtrr/main.c
@@ -248,6 +248,18 @@ set_mtrr(unsigned int reg, unsigned long
unsigned long flags;
int cpu;
+#ifdef CONFIG_SMP
+ /*
+ * If we are not yet online, then there can be no stop_machine() in
+ * parallel. Stop machine ensures this by using get_online_cpus().
+ *
+ * If we are online, then we need to prevent a stop_machine() happening
+ * in parallel by taking the stop cpus mutex.
+ */
+ if (cpu_online(raw_smp_processor_id()))
+ mutex_lock(&stop_cpus_mutex);
+#endif
+
preempt_disable();
data.smp_reg = reg;
@@ -330,6 +342,10 @@ set_mtrr(unsigned int reg, unsigned long
local_irq_restore(flags);
preempt_enable();
+#ifdef CONFIG_SMP
+ if (cpu_online(raw_smp_processor_id()))
+ mutex_unlock(&stop_cpus_mutex);
+#endif
}
/**
Index: linux-2.6-tip/include/linux/stop_machine.h
===================================================================
--- linux-2.6-tip.orig/include/linux/stop_machine.h
+++ linux-2.6-tip/include/linux/stop_machine.h
@@ -27,6 +27,8 @@ struct cpu_stop_work {
struct cpu_stop_done *done;
};
+extern struct mutex stop_cpus_mutex;
+
int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg);
void stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
struct cpu_stop_work *work_buf);
Index: linux-2.6-tip/kernel/stop_machine.c
===================================================================
--- linux-2.6-tip.orig/kernel/stop_machine.c
+++ linux-2.6-tip/kernel/stop_machine.c
@@ -132,8 +132,8 @@ void stop_one_cpu_nowait(unsigned int cp
cpu_stop_queue_work(&per_cpu(cpu_stopper, cpu), work_buf);
}
+DEFINE_MUTEX(stop_cpus_mutex);
/* static data for stop_cpus */
-static DEFINE_MUTEX(stop_cpus_mutex);
static DEFINE_PER_CPU(struct cpu_stop_work, stop_cpus_work);
int __stop_cpus(const struct cpumask *cpumask, cpu_stop_fn_t fn, void *arg)
On Wed, 2011-06-22 at 15:20 -0700, Suresh Siddha wrote:
> +#ifdef CONFIG_SMP
> + /*
> + * If we are not yet online, then there can be no stop_machine() in
> + * parallel. Stop machine ensures this by using get_online_cpus().
> + *
> + * If we are online, then we need to prevent a stop_machine() happening
> + * in parallel by taking the stop cpus mutex.
> + */
> + if (cpu_online(raw_smp_processor_id()))
> + mutex_lock(&stop_cpus_mutex);
> +#endif
This reads like an optimization, is it really worth-while to not take
the mutex in the rare offline case?
On Thu, 23 Jun 2011, Peter Zijlstra wrote:
> On Wed, 2011-06-22 at 15:20 -0700, Suresh Siddha wrote:
> > +#ifdef CONFIG_SMP
> > + /*
> > + * If we are not yet online, then there can be no stop_machine() in
> > + * parallel. Stop machine ensures this by using get_online_cpus().
> > + *
> > + * If we are online, then we need to prevent a stop_machine() happening
> > + * in parallel by taking the stop cpus mutex.
> > + */
> > + if (cpu_online(raw_smp_processor_id()))
> > + mutex_lock(&stop_cpus_mutex);
> > +#endif
>
> This reads like an optimization, is it really worth-while to not take
> the mutex in the rare offline case?
You cannot block on a mutex when you are not online, in fact you
cannot block on it when not active, so the check is wrong anyway.
Thanks,
tglx
On Thu, 2011-06-23 at 11:33 +0200, Thomas Gleixner wrote:
> On Thu, 23 Jun 2011, Peter Zijlstra wrote:
>
> > On Wed, 2011-06-22 at 15:20 -0700, Suresh Siddha wrote:
> > > +#ifdef CONFIG_SMP
> > > + /*
> > > + * If we are not yet online, then there can be no stop_machine() in
> > > + * parallel. Stop machine ensures this by using get_online_cpus().
> > > + *
> > > + * If we are online, then we need to prevent a stop_machine() happening
> > > + * in parallel by taking the stop cpus mutex.
> > > + */
> > > + if (cpu_online(raw_smp_processor_id()))
> > > + mutex_lock(&stop_cpus_mutex);
> > > +#endif
> >
> > This reads like an optimization, is it really worth-while to not take
> > the mutex in the rare offline case?
>
> You cannot block on a mutex when you are not online, in fact you
> cannot block on it when not active, so the check is wrong anyway.
Duh, yeah. Comment totally mislead me.
On that whole active thing, so cpu_active() is brought into life to sort
an cpu-down problem, where we want the lb to stop using a cpu before we
can re-build the sched_domains.
But now we're having trouble because of that on the cpu-up part, where
we update the sched_domains too late (CPU_ONLINE) and hence also set
cpu_active() too late (again CPU_ONLINE).
Couldn't we update the sched_domain tree on CPU_PREPARE_UP to include
the new cpu and then set cpu_active() right along with cpu_online()?
That would also sort your other wait for active while bringup issue..
Note, I'll now go and have my morning juice, so the above might be total
crap.
On Thu, 2011-06-23 at 02:33 -0700, Thomas Gleixner wrote:
> On Thu, 23 Jun 2011, Peter Zijlstra wrote:
>
> > On Wed, 2011-06-22 at 15:20 -0700, Suresh Siddha wrote:
> > > +#ifdef CONFIG_SMP
> > > + /*
> > > + * If we are not yet online, then there can be no stop_machine() in
> > > + * parallel. Stop machine ensures this by using get_online_cpus().
> > > + *
> > > + * If we are online, then we need to prevent a stop_machine() happening
> > > + * in parallel by taking the stop cpus mutex.
> > > + */
> > > + if (cpu_online(raw_smp_processor_id()))
> > > + mutex_lock(&stop_cpus_mutex);
> > > +#endif
> >
> > This reads like an optimization, is it really worth-while to not take
> > the mutex in the rare offline case?
>
> You cannot block on a mutex when you are not online, in fact you
> cannot block on it when not active, so the check is wrong anyway.
>
Ok. Thanks for educating me on that.
Here we are neither online nor active. So we should be ok. But to be
safe, I changed the online checks to active checks and updated the above
comment also in the new revision.
thanks,
suresh