Commit 50a97723 authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Ben Hutchings

x86, apic: Handle a bad TSC more gracefully

commit b47dcbdc 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: default avatarAndy Lutomirski <luto@amacapital.net>
Cc: Bandan Das <bsd@redhat.com>
Link: http://lkml.kernel.org/r/e2fa274e498c33988efac0ba8b7e3120f7f92d78.1413393027.git.luto@amacapital.netSigned-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent c9f1417b
...@@ -1213,7 +1213,7 @@ void __cpuinit setup_local_APIC(void) ...@@ -1213,7 +1213,7 @@ void __cpuinit setup_local_APIC(void)
unsigned int value, queued; unsigned int value, queued;
int i, j, acked = 0; int i, j, acked = 0;
unsigned long long tsc = 0, ntsc; 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) if (cpu_has_tsc)
rdtscll(tsc); rdtscll(tsc);
...@@ -1310,7 +1310,7 @@ void __cpuinit setup_local_APIC(void) ...@@ -1310,7 +1310,7 @@ void __cpuinit setup_local_APIC(void)
break; break;
} }
if (queued) { if (queued) {
if (cpu_has_tsc) { if (cpu_has_tsc && cpu_khz) {
rdtscll(ntsc); rdtscll(ntsc);
max_loops = (cpu_khz << 10) - (ntsc - tsc); max_loops = (cpu_khz << 10) - (ntsc - tsc);
} else } else
......
...@@ -961,14 +961,17 @@ void __init tsc_init(void) ...@@ -961,14 +961,17 @@ void __init tsc_init(void)
x86_init.timers.tsc_pre_init(); x86_init.timers.tsc_pre_init();
if (!cpu_has_tsc) if (!cpu_has_tsc) {
setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
return; return;
}
tsc_khz = x86_platform.calibrate_tsc(); tsc_khz = x86_platform.calibrate_tsc();
cpu_khz = tsc_khz; cpu_khz = tsc_khz;
if (!tsc_khz) { if (!tsc_khz) {
mark_tsc_unstable("could not calculate TSC khz"); mark_tsc_unstable("could not calculate TSC khz");
setup_clear_cpu_cap(X86_FEATURE_TSC_DEADLINE_TIMER);
return; return;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment