Skip to content

Commit a977c40

Browse files
KAGA-KOKOIngo Molnar
authored andcommitted
x86: TSC make the calibration loop smarter
The last changes made the calibration loop 250ms long which is far too much. Try to do that more clever. Experiments have shown that using a 10ms delay for the PIT based calibration gives us a good enough value. If we have a reference (HPET/PMTIMER) and the result of the PIT and the reference is close enough, then we can break out of the calibration loop on a match right away and use the reference value. Otherwise we just loop 3 times and decide then, which value to take. One caveat is that for virtualized environments the PIT calibration often does not work at all and I found out that 10us is a bit too short as well for the reference to give a sane result. The solution here is to make the last loop longer when the first two PIT calibrations failed. Signed-off-by: Thomas Gleixner <[email protected]> Signed-off-by: Ingo Molnar <[email protected]>
1 parent 827014b commit a977c40

File tree

1 file changed

+58
-37
lines changed

1 file changed

+58
-37
lines changed

arch/x86/kernel/tsc.c

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,14 @@ static unsigned long calc_pmtimer_ref(u64 deltatsc, u64 pm1, u64 pm2)
159159
return (unsigned long) deltatsc;
160160
}
161161

162-
#define CAL_MS 50
162+
#define CAL_MS 10
163163
#define CAL_LATCH (CLOCK_TICK_RATE / (1000 / CAL_MS))
164-
#define CAL_PIT_LOOPS 5000
164+
#define CAL_PIT_LOOPS 1000
165+
166+
#define CAL2_MS 50
167+
#define CAL2_LATCH (CLOCK_TICK_RATE / (1000 / CAL2_MS))
168+
#define CAL2_PIT_LOOPS 5000
169+
165170

166171
/*
167172
* Try to calibrate the TSC against the Programmable
@@ -170,7 +175,7 @@ static unsigned long calc_pmtimer_ref(u64 deltatsc, u64 pm1, u64 pm2)
170175
*
171176
* Return ULONG_MAX on failure to calibrate.
172177
*/
173-
static unsigned long pit_calibrate_tsc(void)
178+
static unsigned long pit_calibrate_tsc(u32 latch, unsigned long ms, int loopmin)
174179
{
175180
u64 tsc, t1, t2, delta;
176181
unsigned long tscmin, tscmax;
@@ -185,8 +190,8 @@ static unsigned long pit_calibrate_tsc(void)
185190
* (LSB then MSB) to begin countdown.
186191
*/
187192
outb(0xb0, 0x43);
188-
outb(CAL_LATCH & 0xff, 0x42);
189-
outb(CAL_LATCH >> 8, 0x42);
193+
outb(latch & 0xff, 0x42);
194+
outb(latch >> 8, 0x42);
190195

191196
tsc = t1 = t2 = get_cycles();
192197

@@ -207,18 +212,18 @@ static unsigned long pit_calibrate_tsc(void)
207212
/*
208213
* Sanity checks:
209214
*
210-
* If we were not able to read the PIT more than PIT_MIN_LOOPS
215+
* If we were not able to read the PIT more than loopmin
211216
* times, then we have been hit by a massive SMI
212217
*
213218
* If the maximum is 10 times larger than the minimum,
214219
* then we got hit by an SMI as well.
215220
*/
216-
if (pitcnt < CAL_PIT_LOOPS || tscmax > 10 * tscmin)
221+
if (pitcnt < loopmin || tscmax > 10 * tscmin)
217222
return ULONG_MAX;
218223

219224
/* Calculate the PIT value */
220225
delta = t2 - t1;
221-
do_div(delta, CAL_MS);
226+
do_div(delta, ms);
222227
return delta;
223228
}
224229

@@ -230,8 +235,8 @@ unsigned long native_calibrate_tsc(void)
230235
{
231236
u64 tsc1, tsc2, delta, ref1, ref2;
232237
unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX;
233-
unsigned long flags;
234-
int hpet = is_hpet_enabled(), i;
238+
unsigned long flags, latch, ms;
239+
int hpet = is_hpet_enabled(), i, loopmin;
235240

236241
/*
237242
* Run 5 calibration loops to get the lowest frequency value
@@ -257,7 +262,13 @@ unsigned long native_calibrate_tsc(void)
257262
* calibration delay loop as we have to wait for a certain
258263
* amount of time anyway.
259264
*/
260-
for (i = 0; i < 5; i++) {
265+
266+
/* Preset PIT loop values */
267+
latch = CAL_LATCH;
268+
ms = CAL_MS;
269+
loopmin = CAL_PIT_LOOPS;
270+
271+
for (i = 0; i < 3; i++) {
261272
unsigned long tsc_pit_khz;
262273

263274
/*
@@ -268,7 +279,7 @@ unsigned long native_calibrate_tsc(void)
268279
*/
269280
local_irq_save(flags);
270281
tsc1 = tsc_read_refs(&ref1, hpet);
271-
tsc_pit_khz = pit_calibrate_tsc();
282+
tsc_pit_khz = pit_calibrate_tsc(latch, ms, loopmin);
272283
tsc2 = tsc_read_refs(&ref2, hpet);
273284
local_irq_restore(flags);
274285

@@ -290,6 +301,35 @@ unsigned long native_calibrate_tsc(void)
290301
tsc2 = calc_pmtimer_ref(tsc2, ref1, ref2);
291302

292303
tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2);
304+
305+
/* Check the reference deviation */
306+
delta = ((u64) tsc_pit_min) * 100;
307+
do_div(delta, tsc_ref_min);
308+
309+
/*
310+
* If both calibration results are inside a 10% window
311+
* then we can be sure, that the calibration
312+
* succeeded. We break out of the loop right away. We
313+
* use the reference value, as it is more precise.
314+
*/
315+
if (delta >= 90 && delta <= 110) {
316+
printk(KERN_INFO
317+
"TSC: PIT calibration matches %s. %d loops\n",
318+
hpet ? "HPET" : "PMTIMER", i + 1);
319+
return tsc_ref_min;
320+
}
321+
322+
/*
323+
* Check whether PIT failed more than once. This
324+
* happens in virtualized environments. We need to
325+
* give the virtual PC a slightly longer timeframe for
326+
* the HPET/PMTIMER to make the result precise.
327+
*/
328+
if (i == 1 && tsc_pit_min == ULONG_MAX) {
329+
latch = CAL2_LATCH;
330+
ms = CAL2_MS;
331+
loopmin = CAL2_PIT_LOOPS;
332+
}
293333
}
294334

295335
/*
@@ -309,7 +349,7 @@ unsigned long native_calibrate_tsc(void)
309349
/* The alternative source failed as well, disable TSC */
310350
if (tsc_ref_min == ULONG_MAX) {
311351
printk(KERN_WARNING "TSC: HPET/PMTIMER calibration "
312-
"failed due to SMI disturbance.\n");
352+
"failed.\n");
313353
return 0;
314354
}
315355

@@ -328,37 +368,18 @@ unsigned long native_calibrate_tsc(void)
328368

329369
/* The alternative source failed, use the PIT calibration value */
330370
if (tsc_ref_min == ULONG_MAX) {
331-
printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed due "
332-
"to SMI disturbance. Using PIT calibration\n");
371+
printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed. "
372+
"Using PIT calibration\n");
333373
return tsc_pit_min;
334374
}
335375

336-
/* Check the reference deviation */
337-
delta = ((u64) tsc_pit_min) * 100;
338-
do_div(delta, tsc_ref_min);
339-
340-
/*
341-
* If both calibration results are inside a 5% window, the we
342-
* use the lower frequency of those as it is probably the
343-
* closest estimate.
344-
*/
345-
if (delta >= 95 && delta <= 105) {
346-
printk(KERN_INFO "TSC: PIT calibration confirmed by %s.\n",
347-
hpet ? "HPET" : "PMTIMER");
348-
printk(KERN_INFO "TSC: using %s calibration value\n",
349-
tsc_pit_min <= tsc_ref_min ? "PIT" :
350-
hpet ? "HPET" : "PMTIMER");
351-
return tsc_pit_min <= tsc_ref_min ? tsc_pit_min : tsc_ref_min;
352-
}
353-
354-
printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n",
355-
hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min);
356-
357376
/*
358377
* The calibration values differ too much. In doubt, we use
359378
* the PIT value as we know that there are PMTIMERs around
360-
* running at double speed.
379+
* running at double speed. At least we let the user know:
361380
*/
381+
printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n",
382+
hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min);
362383
printk(KERN_INFO "TSC: Using PIT calibration value\n");
363384
return tsc_pit_min;
364385
}

0 commit comments

Comments
 (0)