| From b47dcbdc5161d3d5756f430191e2840d9b855492 Mon Sep 17 00:00:00 2001 |
| From: Andy Lutomirski <luto@amacapital.net> |
| Date: Wed, 15 Oct 2014 10:12:07 -0700 |
| Subject: x86, apic: Handle a bad TSC more gracefully |
| |
| From: Andy Lutomirski <luto@amacapital.net> |
| |
| commit b47dcbdc5161d3d5756f430191e2840d9b855492 upstream. |
| |
| If the TSC is unusable or disabled, then this patch fixes: |
| |
| - Confusion while trying to clear old APIC interrupts. |
| - Division by zero and incorrect programming of the TSC deadline |
| timer. |
| |
| This fixes boot if the CPU has a TSC deadline timer but a missing or |
| broken TSC. The failure to boot can be observed with qemu using |
| -cpu qemu64,-tsc,+tsc-deadline |
| |
| This also happens to me in nested KVM for unknown reasons. |
| With this patch, I can boot cleanly (although without a TSC). |
| |
| Signed-off-by: Andy Lutomirski <luto@amacapital.net> |
| Cc: Bandan Das <bsd@redhat.com> |
| Link: http://lkml.kernel.org/r/e2fa274e498c33988efac0ba8b7e3120f7f92d78.1413393027.git.luto@amacapital.net |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/x86/kernel/apic/apic.c | 4 ++-- |
| arch/x86/kernel/tsc.c | 5 ++++- |
| 2 files changed, 6 insertions(+), 3 deletions(-) |
| |
| --- a/arch/x86/kernel/apic/apic.c |
| +++ b/arch/x86/kernel/apic/apic.c |
| @@ -1297,7 +1297,7 @@ void setup_local_APIC(void) |
| unsigned int value, queued; |
| int i, j, acked = 0; |
| unsigned long long tsc = 0, ntsc; |
| - long long max_loops = cpu_khz; |
| + long long max_loops = cpu_khz ? cpu_khz : 1000000; |
| |
| if (cpu_has_tsc) |
| rdtscll(tsc); |
| @@ -1383,7 +1383,7 @@ void setup_local_APIC(void) |
| break; |
| } |
| if (queued) { |
| - if (cpu_has_tsc) { |
| + if (cpu_has_tsc && cpu_khz) { |
| rdtscll(ntsc); |
| max_loops = (cpu_khz << 10) - (ntsc - tsc); |
| } else |
| --- a/arch/x86/kernel/tsc.c |
| +++ b/arch/x86/kernel/tsc.c |
| @@ -1166,14 +1166,17 @@ void __init tsc_init(void) |
| |
| x86_init.timers.tsc_pre_init(); |
| |
| - if (!cpu_has_tsc) |
| + if (!cpu_has_tsc) { |
| + setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER); |
| return; |
| + } |
| |
| tsc_khz = x86_platform.calibrate_tsc(); |
| cpu_khz = tsc_khz; |
| |
| if (!tsc_khz) { |
| mark_tsc_unstable("could not calculate TSC khz"); |
| + setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER); |
| return; |
| } |
| |