Author: avg
Date: Fri May 25 07:33:20 2018
New Revision: 334204
URL: https://svnweb.freebsd.org/changeset/base/334204

Log:
  re-synchronize TSC-s on SMP systems after resume, if necessary
  
  The TSC-s are checked and synchronized only if they were good
  originally.  That is, invariant, synchronized, etc.
  
  This is necessary on an AMD-based system where after a wakeup from STR I
  see that BSP clock differs from AP clocks by a count that roughly
  corresponds to one second.  The APs are in sync with each other.  Not
  sure if this is a hardware quirk or a firmware bug.
  
  This is what I see after a resume with this change:
      SMP: passed TSC synchronization test after adjustment
      acpi_timer0: restoring timecounter, ACPI-fast -> TSC-low
  
  Reviewed by:  kib
  MFC after:    3 weeks
  Differential Revision: https://reviews.freebsd.org/D15551

Modified:
  head/sys/amd64/include/clock.h
  head/sys/dev/acpica/acpi.c
  head/sys/i386/include/clock.h
  head/sys/x86/x86/tsc.c

Modified: head/sys/amd64/include/clock.h
==============================================================================
--- head/sys/amd64/include/clock.h      Fri May 25 07:29:52 2018        
(r334203)
+++ head/sys/amd64/include/clock.h      Fri May 25 07:33:20 2018        
(r334204)
@@ -34,6 +34,7 @@ void  clock_init(void);
 
 void   startrtclock(void);
 void   init_TSC(void);
+void   resume_TSC(void);
 
 #define        HAS_TIMER_SPKR 1
 int    timer_spkr_acquire(void);

Modified: head/sys/dev/acpica/acpi.c
==============================================================================
--- head/sys/dev/acpica/acpi.c  Fri May 25 07:29:52 2018        (r334203)
+++ head/sys/dev/acpica/acpi.c  Fri May 25 07:33:20 2018        (r334204)
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/timetc.h>
 
 #if defined(__i386__) || defined(__amd64__)
+#include <machine/clock.h>
 #include <machine/pci_cfgreg.h>
 #endif
 #include <machine/resource.h>
@@ -3040,6 +3041,10 @@ backout:
     if (slp_state >= ACPI_SS_SLP_PREP)
        AcpiLeaveSleepState(state);
     if (slp_state >= ACPI_SS_SLEPT) {
+#if defined(__i386__) || defined(__amd64__)
+       /* NB: we are still using ACPI timecounter at this point. */
+       resume_TSC();
+#endif
        acpi_resync_clock(sc);
        acpi_enable_fixed_events(sc);
     }

Modified: head/sys/i386/include/clock.h
==============================================================================
--- head/sys/i386/include/clock.h       Fri May 25 07:29:52 2018        
(r334203)
+++ head/sys/i386/include/clock.h       Fri May 25 07:33:20 2018        
(r334204)
@@ -32,6 +32,7 @@ void  clock_init(void);
 void   startrtclock(void);
 void   timer_restore(void);
 void   init_TSC(void);
+void   resume_TSC(void);
 
 #define        HAS_TIMER_SPKR 1
 int    timer_spkr_acquire(void);

Modified: head/sys/x86/x86/tsc.c
==============================================================================
--- head/sys/x86/x86/tsc.c      Fri May 25 07:29:52 2018        (r334203)
+++ head/sys/x86/x86/tsc.c      Fri May 25 07:33:20 2018        (r334204)
@@ -451,7 +451,7 @@ adj_smp_tsc(void *arg)
 }
 
 static int
-test_tsc(void)
+test_tsc(int adj_max_count)
 {
        uint64_t *data, *tsc;
        u_int i, size, adj;
@@ -467,7 +467,7 @@ retry:
        smp_tsc = 1;    /* XXX */
        smp_rendezvous(smp_no_rendezvous_barrier, comp_smp_tsc,
            smp_no_rendezvous_barrier, data);
-       if (!smp_tsc && adj < smp_tsc_adjust) {
+       if (!smp_tsc && adj < adj_max_count) {
                adj++;
                smp_rendezvous(smp_no_rendezvous_barrier, adj_smp_tsc,
                    smp_no_rendezvous_barrier, data);
@@ -512,7 +512,7 @@ retry:
  * on uniprocessor kernel.
  */
 static int
-test_tsc(void)
+test_tsc(int adj_max_count __unused)
 {
 
        return (0);
@@ -579,7 +579,7 @@ init_TSC_tc(void)
         * environments, so it is set to a negative quality in those cases.
         */
        if (mp_ncpus > 1)
-               tsc_timecounter.tc_quality = test_tsc();
+               tsc_timecounter.tc_quality = test_tsc(smp_tsc_adjust);
        else if (tsc_is_invariant)
                tsc_timecounter.tc_quality = 1000;
        max_freq >>= tsc_shift;
@@ -614,6 +614,30 @@ init:
        }
 }
 SYSINIT(tsc_tc, SI_SUB_SMP, SI_ORDER_ANY, init_TSC_tc, NULL);
+
+void
+resume_TSC(void)
+{
+       int quality;
+
+       /* If TSC was not good on boot, it is unlikely to become good now. */
+       if (tsc_timecounter.tc_quality < 0)
+               return;
+       /* Nothing to do with UP. */
+       if (mp_ncpus < 2)
+               return;
+
+       /*
+        * If TSC was good, a single synchronization should be enough,
+        * but honour smp_tsc_adjust if it's set.
+        */
+       quality = test_tsc(MAX(smp_tsc_adjust, 1));
+       if (quality != tsc_timecounter.tc_quality) {
+               printf("TSC timecounter quality changed: %d -> %d\n",
+                   tsc_timecounter.tc_quality, quality);
+               tsc_timecounter.tc_quality = quality;
+       }
+}
 
 /*
  * When cpufreq levels change, find out about the (new) max frequency.  We
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to