[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xen stable-4.15] x86/time: improve TSC / CPU freq calibration accuracy



commit ac71fdd9ec8a54240cc09827203197674353a722
Author:     Jan Beulich <jbeulich@xxxxxxxx>
AuthorDate: Tue Jan 25 13:44:55 2022 +0100
Commit:     Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Tue Jan 25 13:44:55 2022 +0100

    x86/time: improve TSC / CPU freq calibration accuracy
    
    While the problem report was for extreme errors, even smaller ones would
    better be avoided: The calculated period to run calibration loops over
    can (and usually will) be shorter than the actual time elapsed between
    first and last platform timer and TSC reads. Adjust values returned from
    the init functions accordingly.
    
    On a Skylake system I've tested this on accuracy (using HPET) went from
    detecting in some cases more than 220kHz too high a value to about
    ±2kHz. On other systems (or on this system, but with PMTMR) the original
    error range was much smaller, with less (in some cases only very little)
    improvement.
    
    Reported-by: James Dingwall <james-xen@xxxxxxxxxxxxxx>
    Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx>
    Reviewed-by: Roger Pau Monné <roger.pau@xxxxxxxxxx>
    master commit: a5c9a80af34eefcd6e31d0ed2b083f452cd9076d
    master date: 2022-01-13 14:31:52 +0100
---
 xen/arch/x86/time.c | 21 +++++++++++++++++++--
 1 file changed, 19 insertions(+), 2 deletions(-)

diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
index 719dc38c7b..0b85b90f55 100644
--- a/xen/arch/x86/time.c
+++ b/xen/arch/x86/time.c
@@ -285,6 +285,23 @@ static char *freq_string(u64 freq)
     return s;
 }
 
+static uint64_t adjust_elapsed(uint64_t elapsed, uint32_t actual,
+                               uint32_t target)
+{
+    if ( likely(actual > target) )
+    {
+        /*
+         * A (perhaps significant) delay before the last timer read (e.g. due
+         * to a SMI or NMI) can lead to (perhaps severe) inaccuracy if not
+         * accounting for the time elapsed beyond the originally calculated
+         * duration of the calibration interval.
+         */
+        elapsed = muldiv64(elapsed, target, actual);
+    }
+
+    return elapsed * CALIBRATE_FRAC;
+}
+
 /************************************************************
  * PLATFORM TIMER 1: PROGRAMMABLE INTERVAL TIMER (LEGACY PIT)
  */
@@ -418,7 +435,7 @@ static int64_t __init init_hpet(struct platform_timesource 
*pts)
     while ( (elapsed = hpet_read32(HPET_COUNTER) - count) < target )
         continue;
 
-    return (rdtsc_ordered() - start) * CALIBRATE_FRAC;
+    return adjust_elapsed(rdtsc_ordered() - start, elapsed, target);
 }
 
 static void resume_hpet(struct platform_timesource *pts)
@@ -468,7 +485,7 @@ static s64 __init init_pmtimer(struct platform_timesource 
*pts)
     while ( (elapsed = (inl(pmtmr_ioport) - count) & mask) < target )
         continue;
 
-    return (rdtsc_ordered() - start) * CALIBRATE_FRAC;
+    return adjust_elapsed(rdtsc_ordered() - start, elapsed, target);
 }
 
 static struct platform_timesource __initdata plt_pmtimer =
--
generated by git-patchbot for /home/xen/git/xen.git#stable-4.15



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.