Commit 8f5a9b18 authored by Andi Kleen's avatar Andi Kleen Committed by Chris Wright

[PATCH] x86_64: Fixing smpboot timing problem

This patch fixes the SMP boot timing problem that hit various people and was
introduced in 2.6.12. Please apply to stable.

>From Eric Biederman

sync_tsc was using smp_call_function to ask the boot processor
to report it's tsc value.  smp_call_function performs an IPI_send_allbutself
which is a broadcast ipi.  There is a window during processor startup during
which the target cpu has started and before it has initialized it's interrupt
vectors so it can properly process an interrupt.  Receveing an interrupt
during that window will triple fault the cpu and do other nasty things.

Why cli does not protect us from that is beyond me.

The simple fix is to match ia64 and provide a smp_call_function_single.
Which avoids the broadcast and is more efficient.

This certainly fixes the problem of getting stuck on boot which was
very easy to trigger on my SMP Hyperthreaded Xeon, and I think
it fixes it for the right reasons.
Signed-off-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarChris Wright <chrisw@osdl.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 4491f6fe
...@@ -283,6 +283,71 @@ struct call_data_struct { ...@@ -283,6 +283,71 @@ struct call_data_struct {
static struct call_data_struct * call_data; static struct call_data_struct * call_data;
/*
* this function sends a 'generic call function' IPI to one other CPU
* in the system.
*/
static void __smp_call_function_single (int cpu, void (*func) (void *info), void *info,
int nonatomic, int wait)
{
struct call_data_struct data;
int cpus = 1;
data.func = func;
data.info = info;
atomic_set(&data.started, 0);
data.wait = wait;
if (wait)
atomic_set(&data.finished, 0);
call_data = &data;
wmb();
/* Send a message to all other CPUs and wait for them to respond */
send_IPI_mask(cpumask_of_cpu(cpu), CALL_FUNCTION_VECTOR);
/* Wait for response */
while (atomic_read(&data.started) != cpus)
cpu_relax();
if (!wait)
return;
while (atomic_read(&data.finished) != cpus)
cpu_relax();
}
/*
* Run a function on another CPU
* <func> The function to run. This must be fast and non-blocking.
* <info> An arbitrary pointer to pass to the function.
* <nonatomic> Currently unused.
* <wait> If true, wait until function has completed on other CPUs.
* [RETURNS] 0 on success, else a negative status code.
*
* Does not return until the remote CPU is nearly ready to execute <func>
* or is or has executed.
*/
int smp_call_function_single (int cpu, void (*func) (void *info), void *info,
int nonatomic, int wait)
{
int me = get_cpu(); /* prevent preemption and reschedule on another processor */
if (cpu == me) {
printk("%s: trying to call self\n", __func__);
put_cpu();
return -EBUSY;
}
spin_lock_bh(&call_lock);
__smp_call_function_single(cpu, func,info,nonatomic,wait);
spin_unlock_bh(&call_lock);
put_cpu();
return 0;
}
/* /*
* this function sends a 'generic call function' IPI to all other CPUs * this function sends a 'generic call function' IPI to all other CPUs
* in the system. * in the system.
......
...@@ -202,9 +202,6 @@ static __cpuinit void sync_master(void *arg) ...@@ -202,9 +202,6 @@ static __cpuinit void sync_master(void *arg)
{ {
unsigned long flags, i; unsigned long flags, i;
if (smp_processor_id() != boot_cpu_id)
return;
go[MASTER] = 0; go[MASTER] = 0;
local_irq_save(flags); local_irq_save(flags);
...@@ -253,7 +250,7 @@ get_delta(long *rt, long *master) ...@@ -253,7 +250,7 @@ get_delta(long *rt, long *master)
return tcenter - best_tm; return tcenter - best_tm;
} }
static __cpuinit void sync_tsc(void) static __cpuinit void sync_tsc(unsigned int master)
{ {
int i, done = 0; int i, done = 0;
long delta, adj, adjust_latency = 0; long delta, adj, adjust_latency = 0;
...@@ -267,9 +264,17 @@ static __cpuinit void sync_tsc(void) ...@@ -267,9 +264,17 @@ static __cpuinit void sync_tsc(void)
} t[NUM_ROUNDS] __cpuinitdata; } t[NUM_ROUNDS] __cpuinitdata;
#endif #endif
printk(KERN_INFO "CPU %d: Syncing TSC to CPU %u.\n",
smp_processor_id(), master);
go[MASTER] = 1; go[MASTER] = 1;
smp_call_function(sync_master, NULL, 1, 0); /* It is dangerous to broadcast IPI as cpus are coming up,
* as they may not be ready to accept them. So since
* we only need to send the ipi to the boot cpu direct
* the message, and avoid the race.
*/
smp_call_function_single(master, sync_master, NULL, 1, 0);
while (go[MASTER]) /* wait for master to be ready */ while (go[MASTER]) /* wait for master to be ready */
no_cpu_relax(); no_cpu_relax();
...@@ -313,16 +318,14 @@ static __cpuinit void sync_tsc(void) ...@@ -313,16 +318,14 @@ static __cpuinit void sync_tsc(void)
printk(KERN_INFO printk(KERN_INFO
"CPU %d: synchronized TSC with CPU %u (last diff %ld cycles, " "CPU %d: synchronized TSC with CPU %u (last diff %ld cycles, "
"maxerr %lu cycles)\n", "maxerr %lu cycles)\n",
smp_processor_id(), boot_cpu_id, delta, rt); smp_processor_id(), master, delta, rt);
} }
static void __cpuinit tsc_sync_wait(void) static void __cpuinit tsc_sync_wait(void)
{ {
if (notscsync || !cpu_has_tsc) if (notscsync || !cpu_has_tsc)
return; return;
printk(KERN_INFO "CPU %d: Syncing TSC to CPU %u.\n", smp_processor_id(), sync_tsc(0);
boot_cpu_id);
sync_tsc();
} }
static __init int notscsync_setup(char *s) static __init int notscsync_setup(char *s)
......
...@@ -46,6 +46,8 @@ extern int pic_mode; ...@@ -46,6 +46,8 @@ extern int pic_mode;
extern int smp_num_siblings; extern int smp_num_siblings;
extern void smp_flush_tlb(void); extern void smp_flush_tlb(void);
extern void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs); extern void smp_message_irq(int cpl, void *dev_id, struct pt_regs *regs);
extern int smp_call_function_single (int cpuid, void (*func) (void *info), void *info,
int retry, int wait);
extern void smp_send_reschedule(int cpu); extern void smp_send_reschedule(int cpu);
extern void smp_invalidate_rcv(void); /* Process an NMI */ extern void smp_invalidate_rcv(void); /* Process an NMI */
extern void zap_low_mappings(void); extern void zap_low_mappings(void);
......
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