x86, apic: Handle a bad TSC more gracefully
authorAndy Lutomirski <luto@amacapital.net>
Wed, 15 Oct 2014 17:12:07 +0000 (10:12 -0700)
committerBen Hutchings <ben@decadent.org.uk>
Sun, 14 Dec 2014 16:23:50 +0000 (16:23 +0000)
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: Ben Hutchings <ben@decadent.org.uk>
arch/x86/kernel/apic/apic.c
arch/x86/kernel/tsc.c

index 0392927..838a3b4 100644 (file)
@@ -1213,7 +1213,7 @@ void __cpuinit 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);
@@ -1310,7 +1310,7 @@ void __cpuinit 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
index 1ec515b..9f3706e 100644 (file)
@@ -961,14 +961,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;
        }