svn commit: r334204 - in head/sys: amd64/include dev/acpica i386/include x86/x86
Andriy Gapon
avg at FreeBSD.org
Fri May 25 07:33:22 UTC 2018
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
More information about the svn-src-head
mailing list