115
115
#define TIMESYNC_MAX_SAMPLES 4
116
116
#define TIMESYNC_GOOD_SAMPLE_THRESHOLD 2000
118
/* Once the error drops below TIMESYNC_PLL_ACTIVATE, activate the PLL.
119
* 500ppm error acumulated over a 60 second interval can produce 30ms of
121
#define TIMESYNC_PLL_ACTIVATE (30 * 1000) /* 30ms. */
122
/* If the error goes above TIMESYNC_PLL_UNSYNC, deactivate the PLL. */
123
#define TIMESYNC_PLL_UNSYNC (2 * TIMESYNC_PLL_ACTIVATE)
124
/* Period during which the frequency error of guest time is measured. */
125
#define TIMESYNC_CALIBRATION_DURATION (15 * 60 * US_PER_SEC) /* 15min. */
118
127
typedef enum TimeSyncState {
119
128
TIMESYNC_INITIALIZING,
120
129
TIMESYNC_STOPPED,
121
130
TIMESYNC_RUNNING,
133
typedef enum TimeSyncSlewState {
134
TimeSyncUncalibrated,
124
139
typedef struct TimeSyncData {
126
gboolean slewCorrection;
127
uint32 slewPercentCorrection;
128
uint32 timeSyncPeriod; /* In seconds. */
141
gboolean slewCorrection;
142
uint32 slewPercentCorrection;
143
uint32 timeSyncPeriod; /* In seconds. */
145
TimeSyncSlewState slewState;
134
150
static void TimeSyncSetSlewState(TimeSyncData *data, gboolean active);
151
static void TimeSyncResetSlew(TimeSyncData *data);
137
154
* Read the time reported by the Host OS.
374
* Slew the guest OS time advancement to correct the time. Only correct a
375
* portion of the error to avoid overcorrection.
391
* Slew the guest OS time advancement to correct the time.
393
* In addition to standard slewing (implemented via TimeSync_Slew), we
394
* also support using an NTP style PLL to slew the time. The PLL can take
395
* a while to end up with an accurate measurement of the frequency error,
396
* so before entering PLL mode we calibrate the frequency error over a
397
* period of TIMESYNC_PLL_ACTIVATE seconds.
399
* When using standard slewing, only correct slewPercentCorrection of the
400
* error. This is to avoid overcorrection when the error is mis-measured,
401
* or overcorrection caused by the daemon waking up later than it is
402
* supposed to leaving the slew in place for longer than anticpiated.
377
404
* @param[in] data Structure tracking time sync state.
378
405
* @param[in] adjustment Amount to correct the guest time.
382
409
TimeSyncSlewTime(TimeSyncData *data, int64 adjustment)
411
static int64 calibrationStart;
412
static int64 calibrationAdjustment;
384
416
int64 timeSyncPeriodUS = data->timeSyncPeriod * US_PER_SEC;
385
417
int64 slewDiff = (adjustment * data->slewPercentCorrection) / 100;
387
return TimeSync_EnableTimeSlew(slewDiff, timeSyncPeriodUS);
419
if (!TimeSync_GetCurrentTime(&now)) {
423
if (adjustment > TIMESYNC_PLL_UNSYNC &&
424
data->slewState != TimeSyncUncalibrated) {
425
g_debug("Adjustment too large (%"FMT64"d), resetting PLL state.\n",
427
data->slewState = TimeSyncUncalibrated;
430
if (data->slewState == TimeSyncUncalibrated) {
431
g_debug("Slewing time: adjustment %"FMT64"d\n", adjustment);
432
if (!TimeSync_Slew(slewDiff, timeSyncPeriodUS, &remaining)) {
433
data->slewState = TimeSyncUncalibrated;
436
if (adjustment < TIMESYNC_PLL_ACTIVATE && TimeSync_PLLSupported()) {
437
g_debug("Starting PLL calibration.\n");
438
calibrationStart = now;
439
/* Starting out the calibration period we are adjustment behind,
440
* but have already requested to correct slewDiff of that. */
441
calibrationAdjustment = slewDiff - adjustment;
442
data->slewState = TimeSyncCalibrating;
444
} else if (data->slewState == TimeSyncCalibrating) {
445
if (now > calibrationStart + TIMESYNC_CALIBRATION_DURATION) {
447
/* Reset slewing to nominal and find out remaining slew. */
448
TimeSync_Slew(0, timeSyncPeriodUS, &remaining);
449
calibrationAdjustment += adjustment;
450
calibrationAdjustment -= remaining;
451
ppmErr = ((1000000 * calibrationAdjustment) << 16) /
452
(now - calibrationStart);
453
if (ppmErr >> 16 < 500 && ppmErr >> 16 > -500) {
454
g_debug("Activating PLL ppmEst=%"FMT64"d (%"FMT64"d)\n",
455
ppmErr >> 16, ppmErr);
456
TimeSync_PLLUpdate(adjustment);
457
TimeSync_PLLSetFrequency(ppmErr);
458
data->slewState = TimeSyncPLL;
460
/* PPM error is too large to try the PLL. */
461
g_debug("PPM error too large: %"FMT64"d (%"FMT64"d) "
462
"not activating PLL\n", ppmErr >> 16, ppmErr);
463
data->slewState = TimeSyncUncalibrated;
466
g_debug("Calibrating error: adjustment %"FMT64"d\n", adjustment);
467
if (!TimeSync_Slew(slewDiff, timeSyncPeriodUS, &remaining)) {
470
calibrationAdjustment += slewDiff;
471
calibrationAdjustment -= remaining;
474
ASSERT(data->slewState == TimeSyncPLL);
475
g_debug("Updating PLL: adjustment %"FMT64"d\n", adjustment);
476
if (!TimeSync_PLLUpdate(adjustment)) {
477
TimeSyncResetSlew(data);
398
491
TimeSyncResetSlew(TimeSyncData *data)
400
TimeSyncSlewTime(data, 0);
494
int64 timeSyncPeriodUS = data->timeSyncPeriod * US_PER_SEC;
495
data->slewState = TimeSyncUncalibrated;
496
TimeSync_Slew(0, timeSyncPeriodUS, &remaining);
497
if (TimeSync_PLLSupported()) {
498
TimeSync_PLLUpdate(0);
499
TimeSync_PLLSetFrequency(0);