diff options
author | Shaohua Li <shaohua.li@intel.com> | 2011-01-09 20:38:12 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2011-01-12 12:47:34 -0500 |
commit | 2a2d31c8dc6f1ebcf5eab1d93a0cb0fb4ed57c7c (patch) | |
tree | 24a83ac74e876bac402a99262ac5ac89144810b3 /drivers/idle/intel_idle.c | |
parent | 5392083748a340f68052c0b83a7ce28b10324972 (diff) |
intel_idle: open broadcast clock event
Intel_idle driver uses CLOCK_EVT_NOTIFY_BROADCAST_ENTER
CLOCK_EVT_NOTIFY_BROADCAST_EXIT
for broadcast clock events. The _ENTER/_EXIT doesn't really open broadcast clock
events, please see processor_idle.c for an example. In some situation, this will
cause boot hang, because some CPUs enters idle but local APIC timer stalls.
Reported-and-tested-by: Yan Zheng <zheng.z.yan@intel.com>
Signed-off-by: Shaohua Li <shaohua.li@intel.com>
cc: stable@kernel.org
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/idle/intel_idle.c')
-rw-r--r-- | drivers/idle/intel_idle.c | 47 |
1 files changed, 46 insertions, 1 deletions
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 8256309deaad..fc393586cc70 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c | |||
@@ -59,6 +59,8 @@ | |||
59 | #include <linux/hrtimer.h> /* ktime_get_real() */ | 59 | #include <linux/hrtimer.h> /* ktime_get_real() */ |
60 | #include <trace/events/power.h> | 60 | #include <trace/events/power.h> |
61 | #include <linux/sched.h> | 61 | #include <linux/sched.h> |
62 | #include <linux/notifier.h> | ||
63 | #include <linux/cpu.h> | ||
62 | #include <asm/mwait.h> | 64 | #include <asm/mwait.h> |
63 | 65 | ||
64 | #define INTEL_IDLE_VERSION "0.4" | 66 | #define INTEL_IDLE_VERSION "0.4" |
@@ -73,6 +75,7 @@ static int max_cstate = MWAIT_MAX_NUM_CSTATES - 1; | |||
73 | 75 | ||
74 | static unsigned int mwait_substates; | 76 | static unsigned int mwait_substates; |
75 | 77 | ||
78 | #define LAPIC_TIMER_ALWAYS_RELIABLE 0xFFFFFFFF | ||
76 | /* Reliable LAPIC Timer States, bit 1 for C1 etc. */ | 79 | /* Reliable LAPIC Timer States, bit 1 for C1 etc. */ |
77 | static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ | 80 | static unsigned int lapic_timer_reliable_states = (1 << 1); /* Default to only C1 */ |
78 | 81 | ||
@@ -252,6 +255,39 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state) | |||
252 | return usec_delta; | 255 | return usec_delta; |
253 | } | 256 | } |
254 | 257 | ||
258 | static void __setup_broadcast_timer(void *arg) | ||
259 | { | ||
260 | unsigned long reason = (unsigned long)arg; | ||
261 | int cpu = smp_processor_id(); | ||
262 | |||
263 | reason = reason ? | ||
264 | CLOCK_EVT_NOTIFY_BROADCAST_ON : CLOCK_EVT_NOTIFY_BROADCAST_OFF; | ||
265 | |||
266 | clockevents_notify(reason, &cpu); | ||
267 | } | ||
268 | |||
269 | static int __cpuinit setup_broadcast_cpuhp_notify(struct notifier_block *n, | ||
270 | unsigned long action, void *hcpu) | ||
271 | { | ||
272 | int hotcpu = (unsigned long)hcpu; | ||
273 | |||
274 | switch (action & 0xf) { | ||
275 | case CPU_ONLINE: | ||
276 | smp_call_function_single(hotcpu, __setup_broadcast_timer, | ||
277 | (void *)true, 1); | ||
278 | break; | ||
279 | case CPU_DOWN_PREPARE: | ||
280 | smp_call_function_single(hotcpu, __setup_broadcast_timer, | ||
281 | (void *)false, 1); | ||
282 | break; | ||
283 | } | ||
284 | return NOTIFY_OK; | ||
285 | } | ||
286 | |||
287 | static struct notifier_block __cpuinitdata setup_broadcast_notifier = { | ||
288 | .notifier_call = setup_broadcast_cpuhp_notify, | ||
289 | }; | ||
290 | |||
255 | /* | 291 | /* |
256 | * intel_idle_probe() | 292 | * intel_idle_probe() |
257 | */ | 293 | */ |
@@ -314,7 +350,11 @@ static int intel_idle_probe(void) | |||
314 | } | 350 | } |
315 | 351 | ||
316 | if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ | 352 | if (boot_cpu_has(X86_FEATURE_ARAT)) /* Always Reliable APIC Timer */ |
317 | lapic_timer_reliable_states = 0xFFFFFFFF; | 353 | lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; |
354 | else { | ||
355 | smp_call_function(__setup_broadcast_timer, (void *)true, 1); | ||
356 | register_cpu_notifier(&setup_broadcast_notifier); | ||
357 | } | ||
318 | 358 | ||
319 | pr_debug(PREFIX "v" INTEL_IDLE_VERSION | 359 | pr_debug(PREFIX "v" INTEL_IDLE_VERSION |
320 | " model 0x%X\n", boot_cpu_data.x86_model); | 360 | " model 0x%X\n", boot_cpu_data.x86_model); |
@@ -441,6 +481,11 @@ static void __exit intel_idle_exit(void) | |||
441 | intel_idle_cpuidle_devices_uninit(); | 481 | intel_idle_cpuidle_devices_uninit(); |
442 | cpuidle_unregister_driver(&intel_idle_driver); | 482 | cpuidle_unregister_driver(&intel_idle_driver); |
443 | 483 | ||
484 | if (lapic_timer_reliable_states != LAPIC_TIMER_ALWAYS_RELIABLE) { | ||
485 | smp_call_function(__setup_broadcast_timer, (void *)false, 1); | ||
486 | unregister_cpu_notifier(&setup_broadcast_notifier); | ||
487 | } | ||
488 | |||
444 | return; | 489 | return; |
445 | } | 490 | } |
446 | 491 | ||