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 | |
| 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>
| -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 | ||
