x86: prevent stale state of c1e_mask across CPU offline/online
authorThomas Gleixner <tglx@linutronix.de>
Mon, 22 Sep 2008 16:54:29 +0000 (18:54 +0200)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 23 Sep 2008 09:38:52 +0000 (11:38 +0200)
Impact: hang which happens across CPU offline/online on AMD C1E systems.

When a CPU goes offline then the corresponding bit in the broadcast
mask is cleared. For AMD C1E enabled CPUs we do not reenable the
broadcast when the CPU comes online again as we do not clear the
corresponding bit in the c1e_mask, which keeps track which CPUs
have been switched to broadcast already. So on those !$@#& machines
we never switch back to broadcasting after a CPU offline/online cycle.

Clear the bit when the CPU plays dead.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/kernel/process.c
arch/x86/kernel/process_32.c
arch/x86/kernel/process_64.c
include/asm-x86/idle.h

index 7fc4d5b0a6a0f99a4d1d9c4df685a5a4d3a135bb..2e2247117f6e10a97fbaef02fe8dfac9676ea2c5 100644 (file)
@@ -246,6 +246,14 @@ static int __cpuinit check_c1e_idle(const struct cpuinfo_x86 *c)
        return 1;
 }
 
+static cpumask_t c1e_mask = CPU_MASK_NONE;
+static int c1e_detected;
+
+void c1e_remove_cpu(int cpu)
+{
+       cpu_clear(cpu, c1e_mask);
+}
+
 /*
  * C1E aware idle routine. We check for C1E active in the interrupt
  * pending message MSR. If we detect C1E, then we handle it the same
@@ -253,9 +261,6 @@ static int __cpuinit check_c1e_idle(const struct cpuinfo_x86 *c)
  */
 static void c1e_idle(void)
 {
-       static cpumask_t c1e_mask = CPU_MASK_NONE;
-       static int c1e_detected;
-
        if (need_resched())
                return;
 
index 3b7a1ddcc0bce7eca8c989906fa377d4f0322f50..4b3cfdf542162a544084e48ae4be35e4ec58d62b 100644 (file)
@@ -88,6 +88,7 @@ static void cpu_exit_clear(void)
        cpu_clear(cpu, cpu_callin_map);
 
        numa_remove_cpu(cpu);
+       c1e_remove_cpu(cpu);
 }
 
 /* We don't actually take CPU down, just spin without interrupts. */
index 71553b664e2af8601361a9af3413599b752a3e5c..e12e0e4dd2566c2959623d4475053c5fcbcd65d3 100644 (file)
@@ -93,6 +93,8 @@ DECLARE_PER_CPU(int, cpu_state);
 static inline void play_dead(void)
 {
        idle_task_exit();
+       c1e_remove_cpu(raw_smp_processor_id());
+
        mb();
        /* Ack it */
        __get_cpu_var(cpu_state) = CPU_DEAD;
index d240e5b30a45d198acd26cf251c34930338d4e37..cbb649123612f003542c463ff99e54062653c306 100644 (file)
@@ -10,4 +10,6 @@ void idle_notifier_register(struct notifier_block *n);
 void enter_idle(void);
 void exit_idle(void);
 
+void c1e_remove_cpu(int cpu);
+
 #endif