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
This commit is contained in:
parent
80919a9e8a
commit
546f863d51
@ -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);
|
||||
|
@ -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 @@ acpi_EnterSleepState(struct acpi_softc *sc, int state)
|
||||
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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 @@ test_tsc(void)
|
||||
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 @@ test_tsc(void)
|
||||
* 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;
|
||||
@ -615,6 +615,30 @@ init_TSC_tc(void)
|
||||
}
|
||||
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
|
||||
* use this to update CPU accounting in case it got a lower estimate at boot.
|
||||
|
Loading…
Reference in New Issue
Block a user