aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2005-07-29 17:03:29 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-07-29 18:01:13 -0400
commit3d483f47579461a4715db33c68ef8752e5a97a2d (patch)
tree6a4c9022074426d3551ff1b47c95f2bfe3f41828
parent94d2ac66c12397e2ca7988dbf59f24a966d275cb (diff)
[PATCH] Fix sync_tsc hang
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. Minor changes by AK Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/x86_64/kernel/smp.c63
-rw-r--r--arch/x86_64/kernel/smpboot.c21
-rw-r--r--include/asm-x86_64/smp.h3
3 files changed, 78 insertions, 9 deletions
diff --git a/arch/x86_64/kernel/smp.c b/arch/x86_64/kernel/smp.c
index 6ee74db52303..e5958220d6b8 100644
--- a/arch/x86_64/kernel/smp.c
+++ b/arch/x86_64/kernel/smp.c
@@ -293,6 +293,69 @@ void unlock_ipi_call_lock(void)
293} 293}
294 294
295/* 295/*
296 * this function sends a 'generic call function' IPI to one other CPU
297 * in the system.
298 */
299static void __smp_call_function_single (int cpu, void (*func) (void *info), void *info,
300 int nonatomic, int wait)
301{
302 struct call_data_struct data;
303 int cpus = 1;
304
305 data.func = func;
306 data.info = info;
307 atomic_set(&data.started, 0);
308 data.wait = wait;
309 if (wait)
310 atomic_set(&data.finished, 0);
311
312 call_data = &data;
313 wmb();
314 /* Send a message to all other CPUs and wait for them to respond */
315 send_IPI_mask(cpumask_of_cpu(cpu), CALL_FUNCTION_VECTOR);
316
317 /* Wait for response */
318 while (atomic_read(&data.started) != cpus)
319 cpu_relax();
320
321 if (!wait)
322 return;
323
324 while (atomic_read(&data.finished) != cpus)
325 cpu_relax();
326}
327
328/*
329 * smp_call_function_single - Run a function on another CPU
330 * @func: The function to run. This must be fast and non-blocking.
331 * @info: An arbitrary pointer to pass to the function.
332 * @nonatomic: Currently unused.
333 * @wait: If true, wait until function has completed on other CPUs.
334 *
335 * Retrurns 0 on success, else a negative status code.
336 *
337 * Does not return until the remote CPU is nearly ready to execute <func>
338 * or is or has executed.
339 */
340
341int smp_call_function_single (int cpu, void (*func) (void *info), void *info,
342 int nonatomic, int wait)
343{
344 /* prevent preemption and reschedule on another processor */
345 int me = get_cpu();
346 if (cpu == me) {
347 WARN_ON(1);
348 put_cpu();
349 return -EBUSY;
350 }
351 spin_lock_bh(&call_lock);
352 __smp_call_function_single(cpu, func, info, nonatomic, wait);
353 spin_unlock_bh(&call_lock);
354 put_cpu();
355 return 0;
356}
357
358/*
296 * this function sends a 'generic call function' IPI to all other CPUs 359 * this function sends a 'generic call function' IPI to all other CPUs
297 * in the system. 360 * in the system.
298 */ 361 */
diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c
index 6d23354443c0..6e4807d64d46 100644
--- a/arch/x86_64/kernel/smpboot.c
+++ b/arch/x86_64/kernel/smpboot.c
@@ -211,9 +211,6 @@ static __cpuinit void sync_master(void *arg)
211{ 211{
212 unsigned long flags, i; 212 unsigned long flags, i;
213 213
214 if (smp_processor_id() != 0)
215 return;
216
217 go[MASTER] = 0; 214 go[MASTER] = 0;
218 215
219 local_irq_save(flags); 216 local_irq_save(flags);
@@ -262,7 +259,7 @@ get_delta(long *rt, long *master)
262 return tcenter - best_tm; 259 return tcenter - best_tm;
263} 260}
264 261
265static __cpuinit void sync_tsc(void) 262static __cpuinit void sync_tsc(unsigned int master)
266{ 263{
267 int i, done = 0; 264 int i, done = 0;
268 long delta, adj, adjust_latency = 0; 265 long delta, adj, adjust_latency = 0;
@@ -276,9 +273,17 @@ static __cpuinit void sync_tsc(void)
276 } t[NUM_ROUNDS] __cpuinitdata; 273 } t[NUM_ROUNDS] __cpuinitdata;
277#endif 274#endif
278 275
276 printk(KERN_INFO "CPU %d: Syncing TSC to CPU %u.\n",
277 smp_processor_id(), master);
278
279 go[MASTER] = 1; 279 go[MASTER] = 1;
280 280
281 smp_call_function(sync_master, NULL, 1, 0); 281 /* It is dangerous to broadcast IPI as cpus are coming up,
282 * as they may not be ready to accept them. So since
283 * we only need to send the ipi to the boot cpu direct
284 * the message, and avoid the race.
285 */
286 smp_call_function_single(master, sync_master, NULL, 1, 0);
282 287
283 while (go[MASTER]) /* wait for master to be ready */ 288 while (go[MASTER]) /* wait for master to be ready */
284 no_cpu_relax(); 289 no_cpu_relax();
@@ -322,16 +327,14 @@ static __cpuinit void sync_tsc(void)
322 printk(KERN_INFO 327 printk(KERN_INFO
323 "CPU %d: synchronized TSC with CPU %u (last diff %ld cycles, " 328 "CPU %d: synchronized TSC with CPU %u (last diff %ld cycles, "
324 "maxerr %lu cycles)\n", 329 "maxerr %lu cycles)\n",
325 smp_processor_id(), boot_cpu_id, delta, rt); 330 smp_processor_id(), master, delta, rt);
326} 331}
327 332
328static void __cpuinit tsc_sync_wait(void) 333static void __cpuinit tsc_sync_wait(void)
329{ 334{
330 if (notscsync || !cpu_has_tsc) 335 if (notscsync || !cpu_has_tsc)
331 return; 336 return;
332 printk(KERN_INFO "CPU %d: Syncing TSC to CPU %u.\n", smp_processor_id(), 337 sync_tsc(boot_cpu_id);
333 boot_cpu_id);
334 sync_tsc();
335} 338}
336 339
337static __init int notscsync_setup(char *s) 340static __init int notscsync_setup(char *s)
diff --git a/include/asm-x86_64/smp.h b/include/asm-x86_64/smp.h
index 18ac762e1ab8..de8b57b2b62b 100644
--- a/include/asm-x86_64/smp.h
+++ b/include/asm-x86_64/smp.h
@@ -49,6 +49,9 @@ extern int smp_num_siblings;
49extern void smp_send_reschedule(int cpu); 49extern void smp_send_reschedule(int cpu);
50extern void zap_low_mappings(void); 50extern void zap_low_mappings(void);
51void smp_stop_cpu(void); 51void smp_stop_cpu(void);
52extern int smp_call_function_single(int cpuid, void (*func) (void *info),
53 void *info, int retry, int wait);
54
52extern cpumask_t cpu_sibling_map[NR_CPUS]; 55extern cpumask_t cpu_sibling_map[NR_CPUS];
53extern cpumask_t cpu_core_map[NR_CPUS]; 56extern cpumask_t cpu_core_map[NR_CPUS];
54extern u8 phys_proc_id[NR_CPUS]; 57extern u8 phys_proc_id[NR_CPUS];