]>
Commit | Line | Data |
---|---|---|
4d1e5b62 AF |
1 | From: Martin Wilck <martin.wilck@fujitsu-siemens.com> |
2 | Subject: x86-64: Make APIC timer calibration SMI-safe | |
3 | References: bnc#410452, bnc#535947 | |
4 | ||
5 | APIC timer calibration can be adversely affected if SMI are | |
6 | triggered during calibration. Make the calibration algorithm | |
7 | more robust to detect and workaround this situation. | |
8 | ||
9 | This patch isn't upstream. Explanation from Martin Wilck: | |
10 | ||
11 | "Unfortunately, while the patch got generally positive review, it hasn't been | |
12 | finally accepted. Upstream wanted to see a solution for all affected | |
13 | architectures (in particular, also i386) which was more than we were able to do | |
14 | at the time, given that the pressure had been reduced by finding a BIOS fix." | |
15 | ||
16 | Acked-by: Jean Delvare <jdelvare@suse.de> | |
17 | ||
18 | --- | |
19 | arch/x86/kernel/apic_64.c | 39 +++++++++++++++++++++++++++++++++------ | |
20 | 1 file changed, 33 insertions(+), 6 deletions(-) | |
21 | ||
22 | --- a/arch/x86/kernel/apic_64.c | |
23 | +++ b/arch/x86/kernel/apic_64.c | |
24 | @@ -299,6 +299,31 @@ static void setup_APIC_timer(void) | |
25 | } | |
26 | ||
27 | /* | |
28 | + * Helper function for calibrate_APIC_clock(): Make sure that | |
29 | + * APIC TMCTT and TSC are read at the same time, to reasonable | |
30 | + * accuracy. On any sane system, the retry loop won't need more | |
31 | + * than a single retry, given that the rdtsc/apic_read/rdtsc | |
32 | + * sequence won't take more than a few cycles. | |
33 | + */ | |
34 | + | |
35 | +#define MAX_DIFFERENCE 1000UL | |
36 | +#define MAX_ITER 10 | |
37 | +static inline int | |
38 | +__read_tsc_and_apic(unsigned long *tsc, unsigned *apic) | |
39 | +{ | |
40 | + unsigned long tsc0, tsc1, diff; | |
41 | + int i = 0; | |
42 | + do { | |
43 | + rdtscll(tsc0); | |
44 | + *apic = apic_read(APIC_TMCCT); | |
45 | + rdtscll(tsc1); | |
46 | + diff = tsc1 - tsc0; | |
47 | + } while (diff > MAX_DIFFERENCE && ++i < MAX_ITER); | |
48 | + *tsc = tsc0 + (diff >> 1); | |
49 | + return diff > MAX_DIFFERENCE ? -EIO : 0; | |
50 | +} | |
51 | + | |
52 | +/* | |
53 | * In this function we calibrate APIC bus clocks to the external | |
54 | * timer. Unfortunately we cannot use jiffies and the timer irq | |
55 | * to calibrate, since some later bootup code depends on getting | |
56 | @@ -317,7 +342,7 @@ static int __init calibrate_APIC_clock(v | |
57 | { | |
58 | unsigned apic, apic_start; | |
59 | unsigned long tsc, tsc_start; | |
60 | - int result; | |
61 | + int result, err_start, err; | |
62 | ||
63 | local_irq_disable(); | |
64 | ||
65 | @@ -328,25 +353,27 @@ static int __init calibrate_APIC_clock(v | |
66 | * | |
67 | * No interrupt enable ! | |
68 | */ | |
69 | - __setup_APIC_LVTT(250000000, 0, 0); | |
70 | + __setup_APIC_LVTT(0xffffffff, 0, 0); | |
71 | ||
72 | - apic_start = apic_read(APIC_TMCCT); | |
73 | #ifdef CONFIG_X86_PM_TIMER | |
74 | if (apic_calibrate_pmtmr && pmtmr_ioport) { | |
75 | + apic_start = apic_read(APIC_TMCCT); | |
76 | pmtimer_wait(5000); /* 5ms wait */ | |
77 | apic = apic_read(APIC_TMCCT); | |
78 | result = (apic_start - apic) * 1000L / 5; | |
79 | } else | |
80 | #endif | |
81 | { | |
82 | - rdtscll(tsc_start); | |
83 | + err_start = __read_tsc_and_apic(&tsc_start, &apic_start); | |
84 | ||
85 | do { | |
86 | - apic = apic_read(APIC_TMCCT); | |
87 | - rdtscll(tsc); | |
88 | + err = __read_tsc_and_apic(&tsc, &apic); | |
89 | } while ((tsc - tsc_start) < TICK_COUNT && | |
90 | (apic_start - apic) < TICK_COUNT); | |
91 | ||
92 | + if (err_start || err) | |
93 | + printk(KERN_CRIT "calibrate_APIC_clock: SMI flood - " | |
94 | + "the APIC timer calibration may be wrong!\n"); | |
95 | result = (apic_start - apic) * 1000L * tsc_khz / | |
96 | (tsc - tsc_start); | |
97 | } |