Skip to content

Commit a7ec817

Browse files
ftang1paulmckrcu
authored andcommitted
x86/tsc: Add option to force frequency recalibration with HW timer
The kernel assumes that the TSC frequency which is provided by the hardware / firmware via MSRs or CPUID(0x15) is correct after applying a few basic consistency checks. This disables the TSC recalibration against HPET or PM timer. As a result there is no mechanism to validate that frequency in cases where a firmware or hardware defect is suspected. And there was case that some user used atomic clock to measure the TSC frequency and reported an inaccuracy issue, which was later fixed in firmware. Add an option 'recalibrate' for 'tsc' kernel parameter to force the tsc freq recalibration with HPET or PM timer, and warn if the deviation from previous value is more than about 500 PPM, which provides a way to verify the data from hardware / firmware. There is no functional change to existing work flow. Recently there was a real-world case: "The 40ms/s divergence between TSC and HPET was observed on hardware that is quite recent" [1], on that platform the TSC frequence 1896 MHz was got from CPUID(0x15), and the force-reclibration with HPET/PMTIMER both calibrated out value of 1975 MHz, which also matched with check from software 'chronyd', indicating it's a problem of BIOS or firmware. [Thanks tglx for helping improving the commit log] [ paulmck: Wordsmith Kconfig help text. ] [1]. https://lore.kernel.org/lkml/20221117230910.GI4001@paulmck-ThinkPad-P17-Gen-1/ Signed-off-by: Feng Tang <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Dave Hansen <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Jonathan Corbet <[email protected]> Cc: <[email protected]> Cc: <[email protected]> Signed-off-by: Paul E. McKenney <[email protected]>
1 parent b7082cd commit a7ec817

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6369,6 +6369,10 @@
63696369
in situations with strict latency requirements (where
63706370
interruptions from clocksource watchdog are not
63716371
acceptable).
6372+
[x86] recalibrate: force recalibration against a HW timer
6373+
(HPET or PM timer) on systems whose TSC frequency was
6374+
obtained from HW or FW using either an MSR or CPUID(0x15).
6375+
Warn if the difference is more than 500 ppm.
63726376

63736377
tsc_early_khz= [X86] Skip early TSC calibration and use the given
63746378
value instead. Useful when the early TSC frequency discovery

arch/x86/kernel/tsc.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ static DEFINE_STATIC_KEY_FALSE(__use_tsc);
4848

4949
int tsc_clocksource_reliable;
5050

51+
static int __read_mostly tsc_force_recalibrate;
52+
5153
static u32 art_to_tsc_numerator;
5254
static u32 art_to_tsc_denominator;
5355
static u64 art_to_tsc_offset;
@@ -303,6 +305,8 @@ static int __init tsc_setup(char *str)
303305
mark_tsc_unstable("boot parameter");
304306
if (!strcmp(str, "nowatchdog"))
305307
no_tsc_watchdog = 1;
308+
if (!strcmp(str, "recalibrate"))
309+
tsc_force_recalibrate = 1;
306310
return 1;
307311
}
308312

@@ -1374,6 +1378,25 @@ static void tsc_refine_calibration_work(struct work_struct *work)
13741378
else
13751379
freq = calc_pmtimer_ref(delta, ref_start, ref_stop);
13761380

1381+
/* Will hit this only if tsc_force_recalibrate has been set */
1382+
if (boot_cpu_has(X86_FEATURE_TSC_KNOWN_FREQ)) {
1383+
1384+
/* Warn if the deviation exceeds 500 ppm */
1385+
if (abs(tsc_khz - freq) > (tsc_khz >> 11)) {
1386+
pr_warn("Warning: TSC freq calibrated by CPUID/MSR differs from what is calibrated by HW timer, please check with vendor!!\n");
1387+
pr_info("Previous calibrated TSC freq:\t %lu.%03lu MHz\n",
1388+
(unsigned long)tsc_khz / 1000,
1389+
(unsigned long)tsc_khz % 1000);
1390+
}
1391+
1392+
pr_info("TSC freq recalibrated by [%s]:\t %lu.%03lu MHz\n",
1393+
hpet ? "HPET" : "PM_TIMER",
1394+
(unsigned long)freq / 1000,
1395+
(unsigned long)freq % 1000);
1396+
1397+
return;
1398+
}
1399+
13771400
/* Make sure we're within 1% */
13781401
if (abs(tsc_khz - freq) > tsc_khz/100)
13791402
goto out;
@@ -1407,8 +1430,10 @@ static int __init init_tsc_clocksource(void)
14071430
if (!boot_cpu_has(X86_FEATURE_TSC) || !tsc_khz)
14081431
return 0;
14091432

1410-
if (tsc_unstable)
1411-
goto unreg;
1433+
if (tsc_unstable) {
1434+
clocksource_unregister(&clocksource_tsc_early);
1435+
return 0;
1436+
}
14121437

14131438
if (boot_cpu_has(X86_FEATURE_NONSTOP_TSC_S3))
14141439
clocksource_tsc.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;
@@ -1421,9 +1446,10 @@ static int __init init_tsc_clocksource(void)
14211446
if (boot_cpu_has(X86_FEATURE_ART))
14221447
art_related_clocksource = &clocksource_tsc;
14231448
clocksource_register_khz(&clocksource_tsc, tsc_khz);
1424-
unreg:
14251449
clocksource_unregister(&clocksource_tsc_early);
1426-
return 0;
1450+
1451+
if (!tsc_force_recalibrate)
1452+
return 0;
14271453
}
14281454

14291455
schedule_delayed_work(&tsc_irqwork, 0);

0 commit comments

Comments
 (0)