From 1b0bf0f1f31f7f329a2b3dcd915b388df49686b5 Mon Sep 17 00:00:00 2001 From: Garrett Wollman Date: Thu, 21 Apr 1994 20:39:30 +0000 Subject: [PATCH] Updated to latest version of NTP kernel PLL from Mills. --- sys/kern/kern_clock.c | 533 ++++++++++++++++++++++------------------ sys/kern/kern_ntptime.c | 222 ++++++++++++----- 2 files changed, 455 insertions(+), 300 deletions(-) diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 020ff5e74f..a336f75840 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)kern_clock.c 7.16 (Berkeley) 5/9/91 - * $Id: kern_clock.c,v 1.14 1994/04/02 07:00:57 davidg Exp $ + * $Id: kern_clock.c,v 1.15 1994/04/02 08:39:20 davidg Exp $ */ /* Portions of this software are covered by the following: */ @@ -114,233 +114,235 @@ int ncallout; /* * Phase-lock loop (PLL) definitions * - * The following defines establish the performance envelope of the PLL. - * They specify the maximum phase error (MAXPHASE), maximum frequency - * error (MAXFREQ), minimum interval between updates (MINSEC) and - * maximum interval between updates (MAXSEC). The intent of these bounds - * is to force the PLL to operate within predefined limits in order to - * satisfy correctness assertions. An excursion which exceeds these - * bounds is clamped to the bound and operation proceeds accordingly. In - * practice, this can occur only if something has failed or is operating - * out of tolerance, but otherwise the PLL continues to operate in a - * stable mode. - * - * MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), as - * defined in the NTP specification. CLOCK.MAX establishes the maximum - * time offset allowed before the system time is reset, rather than - * incrementally adjusted. Here, the maximum offset is clamped to - * MAXPHASE only in order to prevent overflow errors due to defective - * protocol implementations. - * - * MAXFREQ reflects the manufacturing frequency tolerance of the CPU - * clock oscillator plus the maximum slew rate allowed by the protocol. - * It should be set to at least the frequency tolerance of the - * oscillator plus 100 ppm for vernier frequency adjustments. If the - * kernel frequency discipline code is installed (PPS_SYNC), the CPU - * oscillator frequency is disciplined to an external source, presumably - * with negligible frequency error, and MAXFREQ can be reduced. - */ -#define MAXPHASE 512000L /* max phase error (us) */ -#ifdef PPS_SYNC -#define MAXFREQ (100L << SHIFT_USEC) /* max freq error (scaled ppm) */ -#else -#define MAXFREQ (200L << SHIFT_USEC) /* max freq error (scaled ppm) */ -#endif /* PPS_SYNC */ -#define MINSEC 16L /* min interval between updates (s) */ -#define MAXSEC 1200L /* max interval between updates (s) */ - -/* * The following variables are read and set by the ntp_adjtime() system - * call. The ntp_pll.status variable defines the synchronization status of - * the system clock, with codes defined in the timex.h header file. The - * time_offset variable is used by the PLL to adjust the system time in - * small increments. The time_constant variable determines the bandwidth - * or "stiffness" of the PLL. The time_tolerance variable is the maximum - * frequency error or tolerance of the CPU clock oscillator and is a - * property of the architecture; however, in principle it could change - * as result of the presence of external discipline signals, for - * instance. The time_precision variable is usually equal to the kernel - * tick variable; however, in cases where a precision clock counter or - * external clock is available, the resolution can be much less than - * this and depend on whether the external clock is working or not. The - * time_maxerror variable is initialized by a ntp_adjtime() call and - * increased by the kernel once each second to reflect the maximum error - * bound growth. The time_esterror variable is set and read by the - * ntp_adjtime() call, but otherwise not used by the kernel. + * call. + * + * time_state shows the state of the system clock, with values defined + * in the timex.h header file. + * + * time_status shows the status of the system clock, with bits defined + * in the timex.h header file. + * + * time_offset is used by the PLL to adjust the system time in small + * increments. + * + * time_constant determines the bandwidth or "stiffness" of the PLL. + * + * time_tolerance determines maximum frequency error or tolerance of the + * CPU clock oscillator and is a property of the architecture; however, + * in principle it could change as result of the presence of external + * discipline signals, for instance. + * + * time_precision is usually equal to the kernel tick variable; however, + * in cases where a precision clock counter or external clock is + * available, the resolution can be much less than this and depend on + * whether the external clock is working or not. + * + * time_maxerror is initialized by a ntp_adjtime() call and increased by + * the kernel once each second to reflect the maximum error + * bound growth. + * + * time_esterror is set and read by the ntp_adjtime() call, but + * otherwise not used by the kernel. */ -/* - use appropriate fields in ntp_pll instead */ -#if 0 -int ntp_pll.status = TIME_BAD; /* clock synchronization status */ -long time_offset = 0; /* time adjustment (us) */ +int time_status = STA_UNSYNC; /* clock status bits */ +int time_state = TIME_OK; /* clock state */ +long time_offset = 0; /* time offset (us) */ long time_constant = 0; /* pll time constant */ long time_tolerance = MAXFREQ; /* frequency tolerance (scaled ppm) */ long time_precision = 1; /* clock precision (us) */ long time_maxerror = MAXPHASE; /* maximum error (us) */ long time_esterror = MAXPHASE; /* estimated error (us) */ -#endif /* * The following variables establish the state of the PLL and the - * residual time and frequency offset of the local clock. The time_phase - * variable is the phase increment and the ntp_pll.frequency variable is the - * frequency increment of the kernel time variable at each tick of the - * clock. The ntp_pll.frequency variable is set via ntp_adjtime() from a value - * stored in a file when the synchronization daemon is first started. - * Its value is retrieved via ntp_adjtime() and written to the file - * about once per hour by the daemon. The time_adj variable is the - * adjustment added to the value of tick at each timer interrupt and is - * recomputed at each timer interrupt. The time_reftime variable is the - * second's portion of the system time on the last call to - * ntp_adjtime(). It is used to adjust the ntp_pll.frequency variable and to - * increase the time_maxerror as the time since last update increases. - * The scale factors are defined in the timex.h header file. + * residual time and frequency offset of the local clock. The scale + * factors are defined in the timex.h header file. + * + * time_phase and time_freq are the phase increment and the frequency + * increment, respectively, of the kernel time variable at each tick of + * the clock. + * + * time_freq is set via ntp_adjtime() from a value stored in a file when + * the synchronization daemon is first started. Its value is retrieved + * via ntp_adjtime() and written to the file about once per hour by the + * daemon. + * + * time_adj is the adjustment added to the value of tick at each timer + * interrupt and is recomputed at each timer interrupt. + * + * time_reftime is the second's portion of the system time on the last + * call to ntp_adjtime(). It is used to adjust the time_freq variable + * and to increase the time_maxerror as the time since last update + * increases. */ long time_phase = 0; /* phase offset (scaled us) */ -#if 0 -long ntp_pll.frequency = 0; /* frequency offset (scaled ppm) */ -#endif +long time_freq = 0; /* frequency offset (scaled ppm) */ long time_adj = 0; /* tick adjust (scaled 1 / hz) */ -long time_reftime; /* time at last adjustment (s) */ +long time_reftime = 0; /* time at last adjustment (s) */ #ifdef PPS_SYNC /* - * The following defines and declarations are used only if a pulse-per- - * second (PPS) signal is available and connected via a modem control - * lead, such as produced by the optional ppsclock feature incorporated - * in the asynch driver. They establish the design parameters of the PPS - * frequency-lock loop used to discipline the CPU clock oscillator to - * the PPS signal. PPS_AVG is the averaging factor for the frequency - * loop. PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum - * intervals, respectively, in seconds as a power of two. The - * PPS_DISPINC is the initial increment to pps_disp at each second. - */ -#define PPS_AVG 2 /* pps averaging constant (shift) */ -#define PPS_SHIFT 2 /* min interval duration (s) (shift) */ -#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */ -#define PPS_DISPINC 0L /* dispersion increment (us/s) */ - -/* - * The pps_time variable contains the time at each calibration as read - * by microtime(). The pps_usec variable is latched from a high - * resolution counter or external clock at pps_time. Here we want the - * hardware counter contents only, not the contents plus the - * time_tv.usec as usual. The pps_ybar variable is the current CPU - * oscillator frequency offset estimate relative to the PPS signal. The - * pps_disp variable is the current error estimate, which is increased - * pps_dispinc once each second. Frequency updates are permitted only - * when pps_disp is below the pps_dispmax threshold. The pps-mf[] array - * is used as a median filter for the frequency estimate and to derive - * the error estimate. + * The following variables are used only if the if the kernel PPS + * discipline code is configured (PPS_SYNC). The scale factors are + * defined in the timex.h header file. + * + * pps_time contains the time at each calibration interval, as read by + * microtime(). + * + * pps_offset is the time offset produced by the time median filter + * pps_tf[], while pps_jitter is the dispersion measured by this + * filter. + * + * pps_freq is the frequency offset produced by the frequency median + * filter pps_ff[], while pps_stabil is the dispersion measured by + * this filter. + * + * pps_usec is latched from a high resolution counter or external clock + * at pps_time. Here we want the hardware counter contents only, not the + * contents plus the time_tv.usec as usual. + * + * pps_valid counts the number of seconds since the last PPS update. It + * is used as a watchdog timer to disable the PPS discipline should the + * PPS signal be lost. + * + * pps_glitch counts the number of seconds since the beginning of an + * offset burst more than tick/2 from current nominal offset. It is used + * mainly to suppress error bursts due to priority conflicts between the + * PPS interrupt and timer interrupt. + * + * pps_count counts the seconds of the calibration interval, the + * duration of which is pps_shift in powers of two. + * + * pps_intcnt counts the calibration intervals for use in the interval- + * adaptation algorithm. It's just too complicated for words. */ struct timeval pps_time; /* kernel time at last interval */ -long pps_usec = 0; /* usec counter at last interval */ -#if 0 -long pps_ybar = 0; /* frequency estimate (scaled ppm) */ -long pps_disp = MAXFREQ; /* dispersion estimate (scaled ppm) */ -#endif -long pps_dispmax = MAXFREQ / 2; /* dispersion threshold */ -long pps_dispinc = PPS_DISPINC; /* pps dispersion increment/sec */ -long pps_mf[] = {0, 0, 0}; /* pps median filter */ - -/* - * The pps_count variable counts the seconds of the calibration - * interval, the duration of which is pps_shift (s) in powers of two. - * The pps_intcnt variable counts the calibration intervals for use in - * the interval-adaptation algorithm. It's just too complicated for - * words. - */ +long pps_offset = 0; /* pps time offset (us) */ +long pps_jitter = MAXTIME; /* pps time dispersion (jitter) (us) */ +long pps_tf[] = {0, 0, 0}; /* pps time offset median filter (us) */ +long pps_freq = 0; /* frequency offset (scaled ppm) */ +long pps_stabil = MAXFREQ; /* frequency dispersion (scaled ppm) */ +long pps_ff[] = {0, 0, 0}; /* frequency offset median filter */ +long pps_usec = 0; /* microsec counter at last interval */ +long pps_valid = PPS_VALID; /* pps signal watchdog counter */ +int pps_glitch = 0; /* pps signal glitch counter */ int pps_count = 0; /* calibration interval counter (s) */ -#if 0 int pps_shift = PPS_SHIFT; /* interval duration (s) (shift) */ -#endif int pps_intcnt = 0; /* intervals at current duration */ /* * PPS signal quality monitors + * + * pps_jitcnt counts the seconds that have been discarded because the + * jitter measured by the time median filter exceeds the limit MAXTIME + * (100 us). + * + * pps_calcnt counts the frequency calibration intervals, which are + * variable from 4 s to 256 s. + * + * pps_errcnt counts the calibration intervals which have been discarded + * because the wander exceeds the limit MAXFREQ (100 ppm) or where the + * calibration interval jitter exceeds two ticks. + * + * pps_stbcnt counts the calibration intervals that have been discarded + * because the frequency wander exceeds the limit MAXFREQ / 4 (25 us). */ -#if 0 -long pps_calcnt; /* calibration intervals */ -long pps_jitcnt; /* jitter limit exceeded */ -long pps_discnt; /* dispersion limit exceeded */ -#endif +long pps_jitcnt = 0; /* jitter limit exceeded */ +long pps_calcnt = 0; /* calibration intervals */ +long pps_errcnt = 0; /* calibration errors */ +long pps_stbcnt = 0; /* stability limit exceeded */ #endif /* PPS_SYNC */ -struct timex ntp_pll = { - 0, /* mode */ - 0, /* offset */ - 0, /* frequency */ - MAXPHASE, /* maxerror */ - MAXPHASE, /* esterror */ - TIME_BAD, /* status */ - 0, /* time_constant */ - 1, /* precision */ - MAXFREQ, /* tolerance */ - 0, /* ybar */ -#ifdef PPS_SYNC - MAXFREQ, /* disp */ - PPS_SHIFT, /* shift */ - 0, /* calcnt */ - 0, /* jitcnt */ - 0 /* discnt */ -#endif -}; +/* XXX none of this stuff works under FreeBSD */ +#ifdef EXT_CLOCK +/* + * External clock definitions + * + * The following definitions and declarations are used only if an + * external clock (HIGHBALL or TPRO) is configured on the system. + */ +#define CLOCK_INTERVAL 30 /* CPU clock update interval (s) */ + +/* + * The clock_count variable is set to CLOCK_INTERVAL at each PPS + * interrupt and decremented once each second. + */ +int clock_count = 0; /* CPU clock counter */ + +#ifdef HIGHBALL +/* + * The clock_offset and clock_cpu variables are used by the HIGHBALL + * interface. The clock_offset variable defines the offset between + * system time and the HIGBALL counters. The clock_cpu variable contains + * the offset between the system clock and the HIGHBALL clock for use in + * disciplining the kernel time variable. + */ +extern struct timeval clock_offset; /* Highball clock offset */ +long clock_cpu = 0; /* CPU clock adjust */ +#endif /* HIGHBALL */ +#endif /* EXT_CLOCK */ /* * hardupdate() - local clock update * * This routine is called by ntp_adjtime() to update the local clock * phase and frequency. This is used to implement an adaptive-parameter, - * first-order, type-II phase-lock loop. The code computes the time - * since the last update and clamps to a maximum (for robustness). Then - * it multiplies by the offset (sorry about the ugly multiply), scales - * by the time constant, and adds to the frequency variable. Then, it - * computes the phase variable as the offset scaled by the time - * constant. Note that all shifts are assumed to be positive. Only - * enough error checking is done to prevent bizarre behavior due to - * overflow problems. + * first-order, type-II phase-lock loop. The code computes new time and + * frequency offsets each time it is called. The hardclock() routine + * amortizes these offsets at each tick interrupt. If the kernel PPS + * discipline code is configured (PPS_SYNC), the PPS signal itself + * determines the new time offset, instead of the calling argument. + * Presumably, calls to ntp_adjtime() occur only when the caller + * believes the local clock is valid within some bound (+-128 ms with + * NTP). If the caller's time is far different than the PPS time, an + * argument will ensue, and it's not clear who will lose. * * For default SHIFT_UPDATE = 12, the offset is limited to +-512 ms, the * maximum interval between updates is 4096 s and the maximum frequency * offset is +-31.25 ms/s. + * + * Note: splclock() is in effect. */ void hardupdate(offset) long offset; { - long mtemp; + long ltemp, mtemp; - if (offset > MAXPHASE) - ntp_pll.offset = MAXPHASE << SHIFT_UPDATE; - else if (offset < -MAXPHASE) - ntp_pll.offset = -(MAXPHASE << SHIFT_UPDATE); + if (!(time_status & STA_PLL) && !(time_status & STA_PPSTIME)) + return; + ltemp = offset; +#ifdef PPS_SYNC + if (time_status & STA_PPSTIME && time_status & STA_PPSSIGNAL) + ltemp = pps_offset; +#endif /* PPS_SYNC */ + if (ltemp > MAXPHASE) + time_offset = MAXPHASE << SHIFT_UPDATE; + else if (ltemp < -MAXPHASE) + time_offset = -(MAXPHASE << SHIFT_UPDATE); else - ntp_pll.offset = offset << SHIFT_UPDATE; + time_offset = ltemp << SHIFT_UPDATE; mtemp = time.tv_sec - time_reftime; time_reftime = time.tv_sec; if (mtemp > MAXSEC) mtemp = 0; /* ugly multiply should be replaced */ - if (offset < 0) - ntp_pll.frequency -= - (-offset * mtemp) >> (ntp_pll.time_constant - + ntp_pll.time_constant - + SHIFT_KF - - SHIFT_USEC); + if (ltemp < 0) + time_freq -= (-ltemp * mtemp) >> (time_constant + + time_constant + SHIFT_KF - SHIFT_USEC); else - ntp_pll.frequency += - (offset * mtemp) >> (ntp_pll.time_constant - + ntp_pll.time_constant - + SHIFT_KF - - SHIFT_USEC); - if (ntp_pll.frequency > ntp_pll.tolerance) - ntp_pll.frequency = ntp_pll.tolerance; - else if (ntp_pll.frequency < -ntp_pll.tolerance) - ntp_pll.frequency = -ntp_pll.tolerance; - if (ntp_pll.status == TIME_BAD) - ntp_pll.status = TIME_OK; + time_freq += (ltemp * mtemp) >> (time_constant + + time_constant + SHIFT_KF - SHIFT_USEC); + if (time_freq > time_tolerance) + time_freq = time_tolerance; + else if (time_freq < -time_tolerance) + time_freq = -time_tolerance; } + + /* * The hz hardware interval timer. * We update the events relating to real time. @@ -485,35 +487,36 @@ hardclock(frame) * priority any longer than necessary. */ { - int delta; + int time_update; if (timedelta == 0) { - delta = tick; + time_update = tick; } else { if (timedelta < 0) { - delta = tick - tickdelta; + time_update = tick - tickdelta; timedelta += tickdelta; } else { - delta = tick + tickdelta; + time_update = tick + tickdelta; timedelta -= tickdelta; } } /* - * Logic from ``Precision Time and Frequency Synchronization - * Using Modified Kernels'' by David L. Mills, University - * of Delaware. + * Compute the phase adjustment. If the low-order bits + * (time_phase) of the update overflow, bump the high-order bits + * (time_update). */ time_phase += time_adj; - if(time_phase <= -FINEUSEC) { - ltemp = -time_phase >> SHIFT_SCALE; - time_phase += ltemp << SHIFT_SCALE; - time_update -= ltemp; - } else if(time_phase >= FINEUSEC) { - ltemp = time_phase >> SHIFT_SCALE; - time_phase -= ltemp << SHIFT_SCALE; - time_update += ltemp; + if (time_phase <= -FINEUSEC) { + ltemp = -time_phase >> SHIFT_SCALE; + time_phase += ltemp << SHIFT_SCALE; + time_update -= ltemp; + } + else if (time_phase >= FINEUSEC) { + ltemp = time_phase >> SHIFT_SCALE; + time_phase -= ltemp << SHIFT_SCALE; + time_update += ltemp; } - time.tv_usec += delta + time_update; + time.tv_usec += time_update; /* * On rollover of the second the phase adjustment to be used for * the next second is calculated. Also, the maximum error is @@ -529,63 +532,109 @@ hardclock(frame) * limited to half the maximum or 15.625 ms/s. */ if (time.tv_usec >= 1000000) { - time.tv_usec -= 1000000; - time.tv_sec++; - ntp_pll.maxerror += ntp_pll.tolerance >> SHIFT_USEC; - if (ntp_pll.offset < 0) { - ltemp = -ntp_pll.offset >> - (SHIFT_KG + ntp_pll.time_constant); - ntp_pll.offset += ltemp; - time_adj = -ltemp << - (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); - } else { - ltemp = ntp_pll.offset >> - (SHIFT_KG + ntp_pll.time_constant); - ntp_pll.offset -= ltemp; - time_adj = ltemp << - (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); - } + time.tv_usec -= 1000000; + time.tv_sec++; + time_maxerror += time_tolerance >> SHIFT_USEC; + if (time_offset < 0) { + ltemp = -time_offset >> + (SHIFT_KG + time_constant); + time_offset += ltemp; + time_adj = -ltemp << + (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); + } else { + ltemp = time_offset >> + (SHIFT_KG + time_constant); + time_offset -= ltemp; + time_adj = ltemp << + (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE); + } #ifdef PPS_SYNC - /* - * Grow the pps error by pps_dispinc ppm and clamp to - * MAXFREQ. The hardpps() routine will pull it down as - * long as the PPS signal is good. - */ - ntp_pll.disp += pps_dispinc; - if (ntp_pll.disp > MAXFREQ) - ntp_pll.disp = MAXFREQ; - ltemp = ntp_pll.frequency + ntp_pll.ybar; + /* + * Gnaw on the watchdog counter and update the frequency + * computed by the pll and the PPS signal. + */ + pps_valid++; + if (pps_valid == PPS_VALID) { + pps_jitter = MAXTIME; + pps_stabil = MAXFREQ; + time_status &= ~(STA_PPSSIGNAL | STA_PPSJITTER | + STA_PPSWANDER | STA_PPSERROR); + } + ltemp = time_freq + pps_freq; #else - ltemp = ntp_pll.frequency; + ltemp = time_freq; #endif /* PPS_SYNC */ - if (ltemp < 0) - time_adj -= -ltemp >> - (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE); - else - time_adj += ltemp >> - (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE); -#if 0 - time_adj += fixtick << (SHIFT_SCALE - SHIFT_HZ); -#endif + if (ltemp < 0) + time_adj -= -ltemp >> + (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE); + else + time_adj += ltemp >> + (SHIFT_USEC + SHIFT_HZ - SHIFT_SCALE); - /* - * When the CPU clock oscillator frequency is not a - * power of two in Hz, the SHIFT_HZ is only an - * approximate scale factor. In the SunOS kernel, this - * results in a PLL gain factor of 1/1.28 = 0.78 what it - * should be. In the following code the overall gain is - * increased by a factor of 1.25, which results in a - * residual error less than 3 percent. - */ - if (hz == 100) { - if (time_adj < 0) - time_adj -= -time_adj >> 2; - else - time_adj += time_adj >> 2; - } + /* + * When the CPU clock oscillator frequency is not a + * power of two in Hz, the SHIFT_HZ is only an + * approximate scale factor. In the SunOS kernel, this + * results in a PLL gain factor of 1/1.28 = 0.78 what it + * should be. In the following code the overall gain is + * increased by a factor of 1.25, which results in a + * residual error less than 3 percent. + */ + /* Same thing applies for FreeBSD --GAW */ + if (hz == 100) { + if (time_adj < 0) + time_adj -= -time_adj >> 2; + else + time_adj += time_adj >> 2; + } + + /* XXX - this is really bogus, but can't be fixed until + xntpd's idea of the system clock is fixed to know how + the user wants leap seconds handled; in the mean time, + we assume that users of NTP are running without proper + leap second support (this is now the default anyway) */ + /* + * Leap second processing. If in leap-insert state at + * the end of the day, the system clock is set back one + * second; if in leap-delete state, the system clock is + * set ahead one second. The microtime() routine or + * external clock driver will insure that reported time + * is always monotonic. The ugly divides should be + * replaced. + */ + switch (time_state) { + + case TIME_OK: + if (time_status & STA_INS) + time_state = TIME_INS; + else if (time_status & STA_DEL) + time_state = TIME_DEL; + break; + + case TIME_INS: + if (time.tv_sec % 86400 == 0) { + time.tv_sec--; + time_state = TIME_OOP; + } + break; + + case TIME_DEL: + if ((time.tv_sec + 1) % 86400 == 0) { + time.tv_sec++; + time_state = TIME_WAIT; + } + break; + + case TIME_OOP: + time_state = TIME_WAIT; + break; + + case TIME_WAIT: + if (!(time_status & (STA_INS | STA_DEL))) + time_state = TIME_OK; + } } } - if (needsoft) { if (CLKF_BASEPRI(&frame)) { /* diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c index ee865062bf..9e50a42df2 100644 --- a/sys/kern/kern_ntptime.c +++ b/sys/kern/kern_ntptime.c @@ -14,13 +14,13 @@ * * ******************************************************************************/ -/* - * $Id$ - */ - /* * Modification history kern_ntptime.c * + * 24 Mar 94 David L. Mills + * Revised syscall interface to include new variables for PPS + * time discipline. + * * 14 Feb 94 David L. Mills * Added code for external clock * @@ -31,7 +31,8 @@ * Created file */ /* - * ntp_gettime(), ntp_adjtime() - precision time interface + * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS + * 4.1.1 and 4.1.3 * * These routines consitute the Network Time Protocol (NTP) interfaces * for user and daemon application programs. The ntp_gettime() routine @@ -45,18 +46,44 @@ #include "param.h" #include "systm.h" #include "kernel.h" -#include "timex.h" #include "proc.h" +#include "timex.h" -struct timex ntp_pll; /* has to be declared somewhere... */ +/* + * The following variables are used by the hardclock() routine in the + * kern_clock.c module and are described in that module. + */ +extern struct timeval time; /* kernel time variable */ +extern int time_state; /* clock state */ +extern int time_status; /* clock status bits */ +extern long time_offset; /* time adjustment (us) */ +extern long time_freq; /* frequency offset (scaled ppm) */ +extern long time_maxerror; /* maximum error (us) */ +extern long time_esterror; /* estimated error (us) */ +extern long time_constant; /* pll time constant */ +extern long time_precision; /* clock precision (us) */ +extern long time_tolerance; /* frequency tolerance (scaled ppm) */ +#ifdef PPS_SYNC +/* + * The following variables are used only if the PPS signal discipline + * is configured in the kernel. + */ +extern int pps_shift; /* interval duration (s) (shift) */ +extern long pps_freq; /* pps frequency offset (scaled ppm) */ +extern long pps_jitter; /* pps jitter (us) */ +extern long pps_stabil; /* pps stability (scaled ppm) */ +extern long pps_jitcnt; /* jitter limit exceeded */ +extern long pps_calcnt; /* calibration intervals */ +extern long pps_errcnt; /* calibration errors */ +extern long pps_stbcnt; /* stability limit exceeded */ +#endif /* PPS_SYNC */ /* * ntp_gettime() - NTP user application interface */ - struct ntp_gettime_args { - struct ntptimeval *tp; + struct ntptimeval *tp; }; int @@ -64,36 +91,92 @@ ntp_gettime(struct proc *p, struct ntp_gettime_args *uap, int *retval) { struct timeval atv; struct ntptimeval ntv; - int error = 0; int s; + int error = 0; if (uap->tp) { s = splclock(); - microtime(&ntv.time); - ntv.maxerror = ntp_pll.maxerror; - ntv.esterror = ntp_pll.esterror; +#ifdef EXT_CLOCK + /* + * The microtime() external clock routine returns a + * status code. If less than zero, we declare an error + * in the clock status word and return the kernel + * (software) time variable. While there are other + * places that call microtime(), this is the only place + * that matters from an application point of view. + */ + if (microtime(&atv) < 0) { + time_status |= STA_CLOCKERR; + ntv.time = time; + } else + time_status &= ~STA_CLOCKERR; +#else /* EXT_CLOCK */ + microtime(&atv); +#endif /* EXT_CLOCK */ + ntv.time = atv; + ntv.maxerror = time_maxerror; + ntv.esterror = time_esterror; (void) splx(s); - error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof (ntv)); + error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, + sizeof (ntv)); } - if (!error) - retval[0] = ntp_pll.status; - return error; -} + if (!error) { + *retval = time_state; -struct ntp_adjtime_args { - struct timex *tp; -}; + /* + * Status word error decode. If any of these conditions + * occur, an error is returned, instead of the status + * word. Most applications will care only about the fact + * the system clock may not be trusted, not about the + * details. + * + * Hardware or software error + */ + if (time_status & (STA_UNSYNC | STA_CLOCKERR)) + *retval = TIME_ERROR; + + /* + * PPS signal lost when either time or frequency + * synchronization requested + */ + if (time_status & (STA_PPSFREQ | STA_PPSTIME) && + !(time_status & STA_PPSSIGNAL)) + *retval = TIME_ERROR; -extern void hardupdate(long); + /* + * PPS jitter exceeded when time synchronization + * requested + */ + if (time_status & STA_PPSTIME && + time_status & STA_PPSJITTER) + *retval = TIME_ERROR; + + /* + * PPS wander exceeded or calibration error when + * frequency synchronization requested + */ + if (time_status & STA_PPSFREQ && + time_status & (STA_PPSWANDER | STA_PPSERROR)) + *retval = TIME_ERROR; + } + return error; +} /* * ntp_adjtime() - NTP daemon application interface */ +struct ntp_adjtime_args { + struct timex *tp; +}; + int -ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval) { +ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval) +{ struct timex ntv; - int s, error; + int modes; + int s; + int error; error = copyin((caddr_t)uap->tp, (caddr_t)&ntv, sizeof(ntv)); if (error) @@ -104,58 +187,81 @@ ntp_adjtime(struct proc *p, struct ntp_adjtime_args *uap, int *retval) { * change anything. Note that there is no error checking here on * the assumption the superuser should know what it is doing. */ - if(ntv.mode != 0 && !(error = suser(p->p_ucred, &p->p_acflag))) - return error ? error : EPERM; + modes = ntv.modes; + if ((modes != 0) + && (error = suser(p->p_cred->pc_ucred, &p->p_acflag))) + return error; s = splclock(); - if (ntv.mode & ADJ_OFFSET) - hardupdate(ntv.offset); - if (ntv.mode & ADJ_FREQUENCY) + if (modes & MOD_FREQUENCY) #ifdef PPS_SYNC - ntp_pll.frequency = ntv.frequency - ntp_pll.ybar; + time_freq = ntv.freq - pps_freq; #else /* PPS_SYNC */ - ntp_pll.frequency = ntv.frequency; + time_freq = ntv.freq; #endif /* PPS_SYNC */ - if (ntv.mode & ADJ_MAXERROR) - ntp_pll.maxerror = ntv.maxerror; - if (ntv.mode & ADJ_ESTERROR) - ntp_pll.esterror = ntv.esterror; - if (ntv.mode & ADJ_STATUS) - if (ntp_pll.status == TIME_OK || ntv.status == TIME_BAD) - ntp_pll.status = ntv.status; - if (ntv.mode & ADJ_TIMECONST) - ntp_pll.time_constant = ntv.time_constant; + if (modes & MOD_MAXERROR) + time_maxerror = ntv.maxerror; + if (modes & MOD_ESTERROR) + time_esterror = ntv.esterror; + if (modes & MOD_STATUS) { + time_status &= STA_RONLY; + time_status |= ntv.status & ~STA_RONLY; + } + if (modes & MOD_TIMECONST) + time_constant = ntv.constant; + if (modes & MOD_OFFSET) + hardupdate(ntv.offset); /* * Retrieve all clock variables */ - if (ntp_pll.offset < 0) - ntv.offset = -(-ntp_pll.offset >> SHIFT_UPDATE); + if (time_offset < 0) + ntv.offset = -(-time_offset >> SHIFT_UPDATE); else - ntv.offset = ntp_pll.offset >> SHIFT_UPDATE; + ntv.offset = time_offset >> SHIFT_UPDATE; #ifdef PPS_SYNC - ntv.frequency = ntp_pll.frequency + ntp_pll.ybar; + ntv.freq = time_freq + pps_freq; #else /* PPS_SYNC */ - ntv.frequency = ntp_pll.frequency; + ntv.freq = time_freq; #endif /* PPS_SYNC */ - ntv.maxerror = ntp_pll.maxerror; - ntv.esterror = ntp_pll.esterror; - ntv.status = ntp_pll.status; - ntv.time_constant = ntp_pll.time_constant; - ntv.precision = ntp_pll.precision; - ntv.tolerance = ntp_pll.tolerance; + ntv.maxerror = time_maxerror; + ntv.esterror = time_esterror; + ntv.status = time_status; + ntv.constant = time_constant; + ntv.precision = time_precision; + ntv.tolerance = time_tolerance; #ifdef PPS_SYNC - ntv.ybar = ntp_pll.ybar; - ntv.disp = ntp_pll.disp; - ntv.shift = ntp_pll.shift; - ntv.calcnt = ntp_pll.calcnt; - ntv.jitcnt = ntp_pll.jitcnt; - ntv.discnt = ntp_pll.discnt; + ntv.shift = pps_shift; + ntv.ppsfreq = pps_freq; + ntv.jitter = pps_jitter >> PPS_AVG; + ntv.stabil = pps_stabil; + ntv.calcnt = pps_calcnt; + ntv.errcnt = pps_errcnt; + ntv.jitcnt = pps_jitcnt; + ntv.stbcnt = pps_stbcnt; #endif /* PPS_SYNC */ (void)splx(s); error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv)); - retval[0] = ntp_pll.status; + if (!error) { + /* + * Status word error decode. See comments in + * ntp_gettime() routine. + */ + retval[0] = time_state; + if (time_status & (STA_UNSYNC | STA_CLOCKERR)) + retval[0] = TIME_ERROR; + if (time_status & (STA_PPSFREQ | STA_PPSTIME) && + !(time_status & STA_PPSSIGNAL)) + retval[0] = TIME_ERROR; + if (time_status & STA_PPSTIME && + time_status & STA_PPSJITTER) + retval[0] = TIME_ERROR; + if (time_status & STA_PPSFREQ && + time_status & (STA_PPSWANDER | STA_PPSERROR)) + retval[0] = TIME_ERROR; + } return error; } + -- 2.20.1