aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2008-06-09 13:15:00 -0400
committerIngo Molnar <mingo@elte.hu>2008-07-08 01:47:18 -0400
commitaa276e1cafb3ce9d01d1e837bcd67e92616013ac (patch)
treed0ecb8fe8ae70fdaed8d97d317199180882671b5
parent00dba56465228825ea806e3a7fc0aa6bba7bdc6c (diff)
x86, clockevents: add C1E aware idle function
C1E on AMD machines is like C3 but without control from the OS. Up to now we disabled the local apic timer for those machines as it stops when the CPU goes into C1E. This excludes those machines from high resolution timers / dynamic ticks, which hurts especially X2 based laptops. The current boot time C1E detection has another, more serious flaw as well: some BIOSes do not enable C1E until the ACPI processor module is loaded. This causes systems to stop working after that point. To work nicely with C1E enabled machines we use a separate idle function, which checks on idle entry whether C1E was enabled in the Interrupt Pending Message MSR. This allows us to do timer broadcasting for C1E and covers the late enablement of C1E as well. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--arch/x86/kernel/apic_32.c5
-rw-r--r--arch/x86/kernel/apic_64.c26
-rw-r--r--arch/x86/kernel/cpu/amd.c30
-rw-r--r--arch/x86/kernel/cpu/amd_64.c25
-rw-r--r--arch/x86/kernel/process.c66
-rw-r--r--include/asm-x86/apic.h3
-rw-r--r--kernel/time/tick-broadcast.c6
7 files changed, 73 insertions, 88 deletions
diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c
index 4b99b1bdeb6c..c44206e731d4 100644
--- a/arch/x86/kernel/apic_32.c
+++ b/arch/x86/kernel/apic_32.c
@@ -64,9 +64,8 @@ static int enable_local_apic __initdata;
64 64
65/* Local APIC timer verification ok */ 65/* Local APIC timer verification ok */
66static int local_apic_timer_verify_ok; 66static int local_apic_timer_verify_ok;
67/* Disable local APIC timer from the kernel commandline or via dmi quirk 67/* Disable local APIC timer from the kernel commandline or via dmi quirk */
68 or using CPU MSR check */ 68static int local_apic_timer_disabled;
69int local_apic_timer_disabled;
70/* Local APIC timer works in C2 */ 69/* Local APIC timer works in C2 */
71int local_apic_timer_c2_ok; 70int local_apic_timer_c2_ok;
72EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok); 71EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok);
diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c
index 0633cfd0dc29..a5cc8447cf4d 100644
--- a/arch/x86/kernel/apic_64.c
+++ b/arch/x86/kernel/apic_64.c
@@ -43,7 +43,7 @@
43#include <mach_ipi.h> 43#include <mach_ipi.h>
44#include <mach_apic.h> 44#include <mach_apic.h>
45 45
46int disable_apic_timer __cpuinitdata; 46static int disable_apic_timer __cpuinitdata;
47static int apic_calibrate_pmtmr __initdata; 47static int apic_calibrate_pmtmr __initdata;
48int disable_apic; 48int disable_apic;
49 49
@@ -422,32 +422,8 @@ void __init setup_boot_APIC_clock(void)
422 setup_APIC_timer(); 422 setup_APIC_timer();
423} 423}
424 424
425/*
426 * AMD C1E enabled CPUs have a real nasty problem: Some BIOSes set the
427 * C1E flag only in the secondary CPU, so when we detect the wreckage
428 * we already have enabled the boot CPU local apic timer. Check, if
429 * disable_apic_timer is set and the DUMMY flag is cleared. If yes,
430 * set the DUMMY flag again and force the broadcast mode in the
431 * clockevents layer.
432 */
433static void __cpuinit check_boot_apic_timer_broadcast(void)
434{
435 if (!disable_apic_timer ||
436 (lapic_clockevent.features & CLOCK_EVT_FEAT_DUMMY))
437 return;
438
439 printk(KERN_INFO "AMD C1E detected late. Force timer broadcast.\n");
440 lapic_clockevent.features |= CLOCK_EVT_FEAT_DUMMY;
441
442 local_irq_enable();
443 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_FORCE,
444 &boot_cpu_physical_apicid);
445 local_irq_disable();
446}
447
448void __cpuinit setup_secondary_APIC_clock(void) 425void __cpuinit setup_secondary_APIC_clock(void)
449{ 426{
450 check_boot_apic_timer_broadcast();
451 setup_APIC_timer(); 427 setup_APIC_timer();
452} 428}
453 429
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index e76b49e7a916..acc891ae5901 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -24,31 +24,6 @@
24extern void vide(void); 24extern void vide(void);
25__asm__(".align 4\nvide: ret"); 25__asm__(".align 4\nvide: ret");
26 26
27#ifdef CONFIG_X86_LOCAL_APIC
28
29/* AMD systems with C1E don't have a working lAPIC timer. Check for that. */
30static __cpuinit int amd_apic_timer_broken(struct cpuinfo_x86 *c)
31{
32 u32 lo, hi;
33
34 if (c->x86 < 0x0F)
35 return 0;
36
37 /* Family 0x0f models < rev F do not have this MSR */
38 if (c->x86 == 0x0f && c->x86_model < 0x40)
39 return 0;
40
41 rdmsr(MSR_K8_INT_PENDING_MSG, lo, hi);
42 if (lo & K8_INTP_C1E_ACTIVE_MASK) {
43 if (smp_processor_id() != boot_cpu_physical_apicid)
44 printk(KERN_INFO "AMD C1E detected late. "
45 "Force timer broadcast.\n");
46 return 1;
47 }
48 return 0;
49}
50#endif
51
52int force_mwait __cpuinitdata; 27int force_mwait __cpuinitdata;
53 28
54static void __cpuinit early_init_amd(struct cpuinfo_x86 *c) 29static void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
@@ -285,11 +260,6 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
285 num_cache_leaves = 3; 260 num_cache_leaves = 3;
286 } 261 }
287 262
288#ifdef CONFIG_X86_LOCAL_APIC
289 if (amd_apic_timer_broken(c))
290 local_apic_timer_disabled = 1;
291#endif
292
293 /* K6s reports MCEs but don't actually have all the MSRs */ 263 /* K6s reports MCEs but don't actually have all the MSRs */
294 if (c->x86 < 6) 264 if (c->x86 < 6)
295 clear_cpu_cap(c, X86_FEATURE_MCE); 265 clear_cpu_cap(c, X86_FEATURE_MCE);
diff --git a/arch/x86/kernel/cpu/amd_64.c b/arch/x86/kernel/cpu/amd_64.c
index f5fc161d8f2a..f8d20588bde9 100644
--- a/arch/x86/kernel/cpu/amd_64.c
+++ b/arch/x86/kernel/cpu/amd_64.c
@@ -110,28 +110,6 @@ static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c)
110#endif 110#endif
111} 111}
112 112
113/* AMD systems with C1E don't have a working lAPIC timer. Check for that. */
114static __cpuinit int amd_apic_timer_broken(struct cpuinfo_x86 *c)
115{
116 u32 lo, hi;
117
118 if (c->x86 < 0x0F)
119 return 0;
120
121 /* Family 0x0f models < rev F do not have this MSR */
122 if (c->x86 == 0x0f && c->x86_model < 0x40)
123 return 0;
124
125 rdmsr(MSR_K8_INT_PENDING_MSG, lo, hi);
126 if (lo & K8_INTP_C1E_ACTIVE_MASK) {
127 if (smp_processor_id() != boot_cpu_physical_apicid)
128 printk(KERN_INFO "AMD C1E detected late. "
129 "Force timer broadcast.\n");
130 return 1;
131 }
132 return 0;
133}
134
135void __cpuinit early_init_amd(struct cpuinfo_x86 *c) 113void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
136{ 114{
137 early_init_amd_mc(c); 115 early_init_amd_mc(c);
@@ -212,9 +190,6 @@ void __cpuinit init_amd(struct cpuinfo_x86 *c)
212 if (c->x86 == 0x10) 190 if (c->x86 == 0x10)
213 amd_enable_pci_ext_cfg(c); 191 amd_enable_pci_ext_cfg(c);
214 192
215 if (amd_apic_timer_broken(c))
216 disable_apic_timer = 1;
217
218 if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) { 193 if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) {
219 unsigned long long tseg; 194 unsigned long long tseg;
220 195
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 9fea14607dfe..68ad3539b143 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -6,6 +6,7 @@
6#include <linux/sched.h> 6#include <linux/sched.h>
7#include <linux/module.h> 7#include <linux/module.h>
8#include <linux/pm.h> 8#include <linux/pm.h>
9#include <linux/clockchips.h>
9 10
10struct kmem_cache *task_xstate_cachep; 11struct kmem_cache *task_xstate_cachep;
11 12
@@ -219,6 +220,68 @@ static int __cpuinit mwait_usable(const struct cpuinfo_x86 *c)
219 return (edx & MWAIT_EDX_C1); 220 return (edx & MWAIT_EDX_C1);
220} 221}
221 222
223/*
224 * Check for AMD CPUs, which have potentially C1E support
225 */
226static int __cpuinit check_c1e_idle(const struct cpuinfo_x86 *c)
227{
228 if (c->x86_vendor != X86_VENDOR_AMD)
229 return 0;
230
231 if (c->x86 < 0x0F)
232 return 0;
233
234 /* Family 0x0f models < rev F do not have C1E */
235 if (c->x86 == 0x0f && c->x86_model < 0x40)
236 return 0;
237
238 return 1;
239}
240
241/*
242 * C1E aware idle routine. We check for C1E active in the interrupt
243 * pending message MSR. If we detect C1E, then we handle it the same
244 * way as C3 power states (local apic timer and TSC stop)
245 */
246static void c1e_idle(void)
247{
248 static cpumask_t c1e_mask = CPU_MASK_NONE;
249 static int c1e_detected;
250
251 if (need_resched())
252 return;
253
254 if (!c1e_detected) {
255 u32 lo, hi;
256
257 rdmsr(MSR_K8_INT_PENDING_MSG, lo, hi);
258 if (lo & K8_INTP_C1E_ACTIVE_MASK) {
259 c1e_detected = 1;
260 mark_tsc_unstable("TSC halt in C1E");
261 printk(KERN_INFO "System has C1E enabled\n");
262 }
263 }
264
265 if (c1e_detected) {
266 int cpu = smp_processor_id();
267
268 if (!cpu_isset(cpu, c1e_mask)) {
269 cpu_set(cpu, c1e_mask);
270 /* Force broadcast so ACPI can not interfere */
271 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_FORCE,
272 &cpu);
273 printk(KERN_INFO "Switch to broadcast mode on CPU%d\n",
274 cpu);
275 }
276 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
277 default_idle();
278 local_irq_disable();
279 clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
280 local_irq_enable();
281 } else
282 default_idle();
283}
284
222void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c) 285void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)
223{ 286{
224#ifdef CONFIG_X86_SMP 287#ifdef CONFIG_X86_SMP
@@ -236,6 +299,9 @@ void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)
236 */ 299 */
237 printk(KERN_INFO "using mwait in idle threads.\n"); 300 printk(KERN_INFO "using mwait in idle threads.\n");
238 pm_idle = mwait_idle; 301 pm_idle = mwait_idle;
302 } else if (check_c1e_idle(c)) {
303 printk(KERN_INFO "using C1E aware idle routine\n");
304 pm_idle = c1e_idle;
239 } else 305 } else
240 pm_idle = default_idle; 306 pm_idle = default_idle;
241} 307}
diff --git a/include/asm-x86/apic.h b/include/asm-x86/apic.h
index be9639a9a186..3c387cda95fa 100644
--- a/include/asm-x86/apic.h
+++ b/include/asm-x86/apic.h
@@ -38,12 +38,9 @@ extern void generic_apic_probe(void);
38extern int apic_verbosity; 38extern int apic_verbosity;
39extern int timer_over_8254; 39extern int timer_over_8254;
40extern int local_apic_timer_c2_ok; 40extern int local_apic_timer_c2_ok;
41extern int local_apic_timer_disabled;
42 41
43extern int apic_runs_main_timer;
44extern int ioapic_force; 42extern int ioapic_force;
45extern int disable_apic; 43extern int disable_apic;
46extern int disable_apic_timer;
47 44
48/* 45/*
49 * Basic functions accessing APICs. 46 * Basic functions accessing APICs.
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 57a1f02e5ec0..67f80c261709 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -30,6 +30,7 @@
30struct tick_device tick_broadcast_device; 30struct tick_device tick_broadcast_device;
31static cpumask_t tick_broadcast_mask; 31static cpumask_t tick_broadcast_mask;
32static DEFINE_SPINLOCK(tick_broadcast_lock); 32static DEFINE_SPINLOCK(tick_broadcast_lock);
33static int tick_broadcast_force;
33 34
34#ifdef CONFIG_TICK_ONESHOT 35#ifdef CONFIG_TICK_ONESHOT
35static void tick_broadcast_clear_oneshot(int cpu); 36static void tick_broadcast_clear_oneshot(int cpu);
@@ -232,10 +233,11 @@ static void tick_do_broadcast_on_off(void *why)
232 CLOCK_EVT_MODE_SHUTDOWN); 233 CLOCK_EVT_MODE_SHUTDOWN);
233 } 234 }
234 if (*reason == CLOCK_EVT_NOTIFY_BROADCAST_FORCE) 235 if (*reason == CLOCK_EVT_NOTIFY_BROADCAST_FORCE)
235 dev->features |= CLOCK_EVT_FEAT_DUMMY; 236 tick_broadcast_force = 1;
236 break; 237 break;
237 case CLOCK_EVT_NOTIFY_BROADCAST_OFF: 238 case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
238 if (cpu_isset(cpu, tick_broadcast_mask)) { 239 if (!tick_broadcast_force &&
240 cpu_isset(cpu, tick_broadcast_mask)) {
239 cpu_clear(cpu, tick_broadcast_mask); 241 cpu_clear(cpu, tick_broadcast_mask);
240 if (td->mode == TICKDEV_MODE_PERIODIC) 242 if (td->mode == TICKDEV_MODE_PERIODIC)
241 tick_setup_periodic(dev, 0); 243 tick_setup_periodic(dev, 0);