From 50ce46a391c458244ac34f3dcaf9bc5af3c2aa28 Mon Sep 17 00:00:00 2001 From: tholo Date: Mon, 29 Jan 1996 23:05:03 +0000 Subject: [PATCH] Add kernel PLL for system clock Add ntp_adjtime() and ntp_gettime() system calls Mostly stolen from FreeBSD --- lib/libc/sys/Makefile.inc | 4 +- sys/conf/files | 1 + sys/kern/init_sysent.c | 8 +- sys/kern/kern_clock.c | 441 ++++++++++++++++++++++++++++++++++---- sys/kern/kern_ntptime.c | 268 +++++++++++++++++++++++ sys/kern/syscalls.c | 4 +- sys/kern/syscalls.master | 4 +- sys/sys/syscall.h | 2 + sys/sys/syscallargs.h | 10 + sys/sys/timex.h | 293 +++++++++++++++++++++++++ 10 files changed, 981 insertions(+), 54 deletions(-) create mode 100644 sys/kern/kern_ntptime.c create mode 100644 sys/sys/timex.h diff --git a/lib/libc/sys/Makefile.inc b/lib/libc/sys/Makefile.inc index 5fc262bef39..dfb229b6016 100644 --- a/lib/libc/sys/Makefile.inc +++ b/lib/libc/sys/Makefile.inc @@ -25,7 +25,9 @@ ASM= accept.o access.o acct.o adjtime.o bind.o chdir.o chflags.o chmod.o \ listen.o lstat.o madvise.o mincore.o minherit.o mkdir.o mkfifo.o \ mknod.o mlock.o \ mount.o mprotect.o msgctl.o msgget.o msgrcv.o msgsnd.o msync.o \ - munlock.o munmap.o nfssvc.o open.o pathconf.o profil.o quotactl.o \ + munlock.o munmap.o nfssvc.o \ + ntp_gettime.o ntp_adjtime.o \ + open.o pathconf.o profil.o quotactl.o \ read.o readlink.o readv.o recvfrom.o recvmsg.o rename.o revoke.o \ rfork.o \ rmdir.o select.o semconfig.o semget.o semop.o sendmsg.o sendto.o \ diff --git a/sys/conf/files b/sys/conf/files index 9c57fdf5e17..a0fbb203aec 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -94,6 +94,7 @@ file kern/kern_fork.c file kern/kern_ktrace.c ktrace file kern/kern_lkm.c lkm file kern/kern_malloc.c +file kern/kern_ntptime.c file kern/kern_physio.c file kern/kern_proc.c file kern/kern_prot.c diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index bd5c709a8ac..5e2b614efd6 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -419,10 +419,10 @@ struct sysent sysent[] = { sys_nosys }, /* 174 = unimplemented */ { 0, 0, sys_nosys }, /* 175 = unimplemented */ - { 0, 0, - sys_nosys }, /* 176 = unimplemented */ - { 0, 0, - sys_nosys }, /* 177 = unimplemented */ + { 1, s(struct sys_ntp_adjtime_args), + sys_ntp_adjtime }, /* 176 = ntp_adjtime */ + { 1, s(struct sys_ntp_gettime_args), + sys_ntp_gettime }, /* 177 = ntp_gettime */ { 0, 0, sys_nosys }, /* 178 = unimplemented */ { 0, 0, diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c index 86592699658..2251d744e26 100644 --- a/sys/kern/kern_clock.c +++ b/sys/kern/kern_clock.c @@ -1,4 +1,4 @@ -/* $NetBSD: kern_clock.c,v 1.24 1996/01/17 04:37:31 cgd Exp $ */ +/* $NetBSD: kern_clock.c,v 1.23 1995/12/28 19:16:41 thorpej Exp $ */ /*- * Copyright (c) 1982, 1986, 1991, 1993 @@ -40,6 +40,23 @@ * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 */ +/* Portions of this software are covered by the following: */ +/****************************************************************************** + * * + * Copyright (c) David L. Mills 1993, 1994 * + * * + * Permission to use, copy, modify, and distribute this software and its * + * documentation for any purpose and without fee is hereby granted, provided * + * that the above copyright notice appears in all copies and that both the * + * copyright notice and this permission notice appear in supporting * + * documentation, and that the name University of Delaware not be used in * + * advertising or publicity pertaining to distribution of the software * + * without specific, written prior permission. The University of Delaware * + * makes no representations about the suitability this software for any * + * purpose. It is provided "as is" without express or implied warranty. * + * * + *****************************************************************************/ + #include #include #include @@ -47,6 +64,7 @@ #include #include #include +#include #include @@ -103,12 +121,211 @@ int profprocs; int ticks; static int psdiv, pscnt; /* prof => stat divider */ int psratio; /* ratio: prof / stat */ -int tickfix, tickfixinterval; /* used if tick not really integral */ -static int tickfixcnt; /* number of ticks since last fix */ volatile struct timeval time; volatile struct timeval mono_time; +/* + * Phase-lock loop (PLL) definitions + * + * The following variables are read and set by the ntp_adjtime() system + * 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. + */ +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) */ + +/* + * The following variables establish the state of the PLL and the + * 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. + */ +static long time_phase = 0; /* phase offset (scaled us) */ +long time_freq = 0; /* frequency offset (scaled ppm) */ +static long time_adj = 0; /* tick adjust (scaled 1 / hz) */ +static long time_reftime = 0; /* time at last adjustment (s) */ + +#ifdef PPS_SYNC +/* + * 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_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) */ +int pps_shift = PPS_SHIFT; /* interval duration (s) (shift) */ +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). + */ +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 */ + +/* + * 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 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 ltemp, mtemp; + + 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 + 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 (ltemp < 0) + time_freq -= (-ltemp * mtemp) >> (time_constant + + time_constant + SHIFT_KF - SHIFT_USEC); + else + 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; +} + /* * Initialize clock frequencies and start both clocks running. */ @@ -142,9 +359,10 @@ hardclock(frame) { register struct callout *p1; register struct proc *p; - register int delta, needsoft; - extern int tickdelta; - extern long timedelta; + register int needsoft; + int time_update; + struct timeval newtime; + long ltemp; /* * Update real-time timeout queue. @@ -189,28 +407,142 @@ hardclock(frame) statclock(frame); /* - * Increment the time-of-day. The increment is normally just - * ``tick''. If the machine is one which has a clock frequency - * such that ``hz'' would not divide the second evenly into - * milliseconds, a periodic adjustment must be applied. Finally, - * if we are still adjusting the time (see adjtime()), - * ``tickdelta'' may also be added in. + * Increment the time-of-day */ ticks++; - delta = tick; - if (tickfix) { - tickfixcnt++; - if (tickfixcnt >= tickfixinterval) { - delta += tickfix; - tickfixcnt = 0; - } + newtime = time; + + if (timedelta == 0) { + time_update = tick; } - if (timedelta != 0) { - delta = tick + tickdelta; + else { + time_update = tick + tickdelta; timedelta -= tickdelta; } - BUMPTIME(&time, delta); - BUMPTIME(&mono_time, delta); + BUMPTIME(&mono_time, time_update); + + /* + * 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; + } + + newtime.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 + * increased by the tolerance. If the PPS frequency discipline + * code is present, the phase is increased to compensate for the + * CPU clock oscillator frequency error. + * + * With SHIFT_SCALE = 23, the maximum frequency adjustment is + * +-256 us per tick, or 25.6 ms/s at a clock frequency of 100 + * Hz. The time contribution is shifted right a minimum of two + * bits, while the frequency contribution is a right shift. + * Thus, overflow is prevented if the frequency contribution is + * limited to half the maximum or 15.625 ms/s. + */ + if (newtime.tv_usec >= 1000000) { + newtime.tv_usec -= 1000000; + newtime.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 + /* + * 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 = 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); + + /* + * 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 following code + * the overall gain is increased by a factor of 1.25. + */ + if (hz == 100) { + if (time_adj < 0) + time_adj -= -time_adj >> 2; + else + time_adj += time_adj >> 2; + } + /* + * 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 + * replacesd. + */ + 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 (newtime.tv_sec % 86400 == 0) { + newtime.tv_sec--; + time_state = TIME_OOP; + } + break; + + case TIME_DEL: + if ((newtime.tv_sec + 1) % 86400 == 0) { + newtime.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; + break; + } + } + time = newtime; /* * Process callouts at a very low cpu priority, so we don't keep the @@ -345,33 +677,53 @@ int hzto(tv) struct timeval *tv; { - register long ticks, sec; + register unsigned long ticks; + register long sec, usec; int s; /* - * If number of microseconds will fit in 32 bit arithmetic, - * then compute number of microseconds to time and scale to - * ticks. Otherwise just compute number of hz in time, rounding - * times greater than representible to maximum value. (We must - * compute in microseconds, because hz can be greater than 1000, - * and thus tick can be less than one millisecond). + * If the number of usecs in the whole seconds part of the time + * difference fits in a long, then the total number of usecs will + * fit in an unsigned long. Compute the total and convert it to + * ticks, rounding up and adding 1 to allow for the current tick + * to expire. Rounding also depends on unsigned long arithmetic + * to avoid overflow. + * + * Otherwise, if the number of ticks in the whole seconds part of + * the time difference fits in a long, then convert the parts to + * ticks separately and add, using similar rounding methods and + * overflow avoidance. This method would work in the previous + * case but it is slightly slower and assumes that hz is integral. + * + * Otherwise, round the time difference down to the maximum + * representable value. * - * Delta times less than 14 hours can be computed ``exactly''. - * (Note that if hz would yeild a non-integral number of us per - * tick, i.e. tickfix is nonzero, timouts can be a tick longer - * than they should be.) Maximum value for any timeout in 10ms - * ticks is 250 days. + * If ints have 32 bits, then the maximum value for any timeout in + * 10ms ticks is 248 days. */ - s = splhigh(); + s = splclock(); sec = tv->tv_sec - time.tv_sec; - if (sec <= 0x7fffffff / 1000000 - 1) - ticks = ((tv->tv_sec - time.tv_sec) * 1000000 + - (tv->tv_usec - time.tv_usec)) / tick; - else if (sec <= 0x7fffffff / hz) - ticks = sec * hz; - else - ticks = 0x7fffffff; + usec = tv->tv_usec - time.tv_usec; splx(s); + if (usec < 0) { + sec--; + usec += 1000000; + } + if (sec < 0) { +#ifdef DIAGNOSTIC + printf("hzto: negative time difference %ld sec %ld usec\n", + sec, usec); +#endif + ticks = 1; + } + else if (sec <= LONG_MAX / 1000000) + ticks = (sec * 1000000 + (unsigned long)usec + (tick - 1)) / tick + 1; + else if (sec <= LONG_MAX / hz) + ticks = sec * hz + ((unsigned long)usec + (tick - 1)) / tick + 1; + else + ticks = LONG_MAX; + if (ticks > INT_MAX) + ticks = INT_MAX; return (ticks); } @@ -539,9 +891,8 @@ sysctl_clockrate(where, sizep) /* * Construct clockinfo structure. */ - clkinfo.tick = tick; - clkinfo.tickadj = tickadj; clkinfo.hz = hz; + clkinfo.tick = tick; clkinfo.profhz = profhz; clkinfo.stathz = stathz ? stathz : hz; return (sysctl_rdstruct(where, sizep, NULL, &clkinfo, sizeof(clkinfo))); diff --git a/sys/kern/kern_ntptime.c b/sys/kern/kern_ntptime.c new file mode 100644 index 00000000000..3a575000db4 --- /dev/null +++ b/sys/kern/kern_ntptime.c @@ -0,0 +1,268 @@ +/****************************************************************************** + * * + * Copyright (c) David L. Mills 1993, 1994 * + * * + * Permission to use, copy, modify, and distribute this software and its * + * documentation for any purpose and without fee is hereby granted, provided * + * that the above copyright notice appears in all copies and that both the * + * copyright notice and this permission notice appear in supporting * + * documentation, and that the name University of Delaware not be used in * + * advertising or publicity pertaining to distribution of the software * + * without specific, written prior permission. The University of Delaware * + * makes no representations about the suitability this software for any * + * purpose. It is provided "as is" without express or implied warranty. * + * * + ******************************************************************************/ + +/* + * Modification history kern_ntptime.c + * + * 24 Sep 94 David L. Mills + * Tightened code at exits. + * + * 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 + * + * 28 Nov 93 David L. Mills + * Revised frequency scaling to conform with adjusted parameters + * + * 17 Sep 93 David L. Mills + * Created file + */ +/* + * ntp_gettime(), ntp_adjtime() - precision time interface for SunOS + * V4.1.1 and V4.1.3 + * + * These routines consitute the Network Time Protocol (NTP) interfaces + * for user and daemon application programs. The ntp_gettime() routine + * provides the time, maximum error (synch distance) and estimated error + * (dispersion) to client user application programs. The ntp_adjtime() + * routine is used by the NTP daemon to adjust the system clock to an + * externally derived time. The time offset and related variables set by + * this routine are used by hardclock() to adjust the phase and + * frequency of the phase-lock loop which controls the system clock. + */ +#include +#include +#include +#include +#include + +#include +#include + +/* + * The following variables are used by the hardclock() routine in the + * kern_clock.c module and are described in that module. + */ +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 + */ +int +sys_ntp_gettime(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_ntp_gettime_args /* { + syscallarg(struct ntptimeval *) tp; + } */ *uap = v; + struct timeval atv; + struct ntptimeval ntv; + int error, s; + + if (SCARG(uap, tp)) { + s = splclock(); +#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)SCARG(uap, tp), + sizeof (ntv)); + } + if (!error) { + + /* + * 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)) || + + /* + * PPS signal lost when either time or frequency + * synchronization requested + */ + (time_status & (STA_PPSFREQ | STA_PPSTIME) && + !(time_status & STA_PPSSIGNAL)) || + + /* + * PPS jitter exceeded when time synchronization + * requested + */ + (time_status & STA_PPSTIME && + time_status & STA_PPSJITTER) || + + /* + * PPS wander exceeded or calibration error when + * frequency synchronization requested + */ + (time_status & STA_PPSFREQ && + time_status & (STA_PPSWANDER | STA_PPSERROR))) + *retval = TIME_ERROR; + else + *retval = time_state; + } + return (error); +} + +/* + * ntp_adjtime() - NTP daemon application interface + */ +int +sys_ntp_adjtime(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_ntp_adjtime_args /* { + syscallarg(struct timex *) tp; + } */ *uap = v; + struct timex ntv; + int modes; + int error, s; + + error = copyin((caddr_t)SCARG(uap, tp), (caddr_t)&ntv, sizeof(ntv)); + if (error) + return (error); + + /* + * Update selected clock variables - only the superuser can + * change anything. Note that there is no error checking here on + * the assumption the superuser should know what it is doing. + */ + modes = ntv.modes; + if (!suser(p->p_ucred, &p->p_acflag) && modes != 0) + return; + s = splclock(); + if (modes & MOD_FREQUENCY) +#ifdef PPS_SYNC + time_freq = ntv.freq - pps_freq; +#else /* PPS_SYNC */ + time_freq = ntv.freq; +#endif /* PPS_SYNC */ + 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 (time_offset < 0) + ntv.offset = -(-time_offset >> SHIFT_UPDATE); + else + ntv.offset = time_offset >> SHIFT_UPDATE; +#ifdef PPS_SYNC + ntv.freq = time_freq + pps_freq; +#else /* PPS_SYNC */ + ntv.freq = time_freq; +#endif /* PPS_SYNC */ + 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.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)SCARG(uap, tp), + sizeof(ntv)); + if (!error) { + + /* + * Status word error decode. See comments in + * ntp_gettime() routine. + */ + if ((time_status & (STA_UNSYNC | STA_CLOCKERR)) || + (time_status & (STA_PPSFREQ | STA_PPSTIME) && + !(time_status & STA_PPSSIGNAL)) || + (time_status & STA_PPSTIME && + time_status & STA_PPSJITTER) || + (time_status & STA_PPSFREQ && + time_status & (STA_PPSWANDER | STA_PPSERROR))) + *retval = TIME_ERROR; + else + *retval = time_state; + } + return (error); +} diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 577d9f1cdb2..1add2d641cd 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -210,8 +210,8 @@ char *syscallnames[] = { "#173 (unimplemented)", /* 173 = unimplemented */ "#174 (unimplemented)", /* 174 = unimplemented */ "#175 (unimplemented)", /* 175 = unimplemented */ - "#176 (unimplemented)", /* 176 = unimplemented */ - "#177 (unimplemented)", /* 177 = unimplemented */ + "ntp_adjtime", /* 176 = ntp_adjtime */ + "ntp_gettime", /* 177 = ntp_gettime */ "#178 (unimplemented)", /* 178 = unimplemented */ "#179 (unimplemented)", /* 179 = unimplemented */ "#180 (unimplemented)", /* 180 = unimplemented */ diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 813c9c5997f..1cf7b06d2a0 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -309,8 +309,8 @@ 173 UNIMPL 174 UNIMPL 175 UNIMPL -176 UNIMPL -177 UNIMPL +176 STD { int sys_ntp_adjtime(struct timex *tp); } +177 STD { int sys_ntp_gettime(struct ntptimeval *tp); } 178 UNIMPL 179 UNIMPL 180 UNIMPL diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index 098fc57ca69..9cbe6ab830e 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -167,6 +167,8 @@ /* 169 is compat_10 osemsys */ /* 170 is compat_10 omsgsys */ /* 171 is compat_10 oshmsys */ +#define SYS_ntp_adjtime 176 +#define SYS_ntp_gettime 177 #define SYS_setgid 181 #define SYS_setegid 182 #define SYS_seteuid 183 diff --git a/sys/sys/syscallargs.h b/sys/sys/syscallargs.h index 40f7bb2f489..00f6192852c 100644 --- a/sys/sys/syscallargs.h +++ b/sys/sys/syscallargs.h @@ -741,6 +741,14 @@ struct compat_10_sys_shmsys_args { syscallarg(int) a4; }; +struct sys_ntp_adjtime_args { + syscallarg(struct timex *) tp; +}; + +struct sys_ntp_gettime_args { + syscallarg(struct ntptimeval *) tp; +}; + struct sys_setgid_args { syscallarg(gid_t) gid; }; @@ -1128,6 +1136,8 @@ int compat_10_sys_msgsys __P((struct proc *, void *, register_t *)); int compat_10_sys_shmsys __P((struct proc *, void *, register_t *)); #else #endif +int sys_ntp_adjtime __P((struct proc *, void *, register_t *)); +int sys_ntp_gettime __P((struct proc *, void *, register_t *)); int sys_setgid __P((struct proc *, void *, register_t *)); int sys_setegid __P((struct proc *, void *, register_t *)); int sys_seteuid __P((struct proc *, void *, register_t *)); diff --git a/sys/sys/timex.h b/sys/sys/timex.h new file mode 100644 index 00000000000..b352b9ea973 --- /dev/null +++ b/sys/sys/timex.h @@ -0,0 +1,293 @@ +/****************************************************************************** + * * + * Copyright (c) David L. Mills 1993, 1994 * + * * + * Permission to use, copy, modify, and distribute this software and its * + * documentation for any purpose and without fee is hereby granted, provided * + * that the above copyright notice appears in all copies and that both the * + * copyright notice and this permission notice appear in supporting * + * documentation, and that the name University of Delaware not be used in * + * advertising or publicity pertaining to distribution of the software * + * without specific, written prior permission. The University of Delaware * + * makes no representations about the suitability this software for any * + * purpose. It is provided "as is" without express or implied warranty. * + * * + ******************************************************************************/ + +/* + * Modification history timex.h + * + * 26 Sep 94 David L. Mills + * Added defines for hybrid phase/frequency-lock loop. + * + * 19 Mar 94 David L. Mills + * Moved defines from kernel routines to header file and added new + * defines for PPS phase-lock loop. + * + * 20 Feb 94 David L. Mills + * Revised status codes and structures for external clock and PPS + * signal discipline. + * + * 28 Nov 93 David L. Mills + * Adjusted parameters to improve stability and increase poll + * interval. + * + * 17 Sep 93 David L. Mills + * Created file + */ +/* + * This header file defines the Network Time Protocol (NTP) interfaces + * for user and daemon application programs. These are implemented using + * private syscalls and data structures and require specific kernel + * support. + * + * NAME + * ntp_gettime - NTP user application interface + * + * SYNOPSIS + * #include + * + * int syscall(SYS_ntp_gettime, tptr) + * + * int SYS_ntp_gettime defined in syscall.h header file + * struct ntptimeval *tptr pointer to ntptimeval structure + * + * NAME + * ntp_adjtime - NTP daemon application interface + * + * SYNOPSIS + * #include + * + * int syscall(SYS_ntp_adjtime, mode, tptr) + * + * int SYS_ntp_adjtime defined in syscall.h header file + * struct timex *tptr pointer to timex structure + * + */ +#ifndef _SYS_TIMEX_H_ +#define _SYS_TIMEX_H_ 1 + +#include + +/* + * The following defines establish the engineering parameters of the + * phase-lock loop (PLL) model used in the kernel implementation. These + * parameters have been carefully chosen by analysis for good stability + * and wide dynamic range. + * + * The hz variable is defined in the kernel build environment. It + * establishes the timer interrupt frequency, 100 Hz for the SunOS + * kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the OSF/1 + * kernel. SHIFT_HZ expresses the same value as the nearest power of two + * in order to avoid hardware multiply operations. + * + * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen + * for a slightly underdamped convergence characteristic. SHIFT_KH + * establishes the damping of the FLL and is chosen by wisdom and black + * art. + * + * MAXTC establishes the maximum time constant of the PLL. With the + * SHIFT_KG and SHIFT_KF values given and a time constant range from + * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours, + * respectively. + */ +#define SHIFT_HZ 7 /* log2(hz) */ +#define SHIFT_KG 6 /* phase factor (shift) */ +#define SHIFT_KF 16 /* PLL frequency factor (shift) */ +#define SHIFT_KH 2 /* FLL frequency factor (shift) */ +#define MAXTC 6 /* maximum time constant (shift) */ + +/* + * The following defines establish the scaling of the various variables + * used by the PLL. They are chosen to allow the greatest precision + * possible without overflow of a 32-bit word. + * + * SHIFT_SCALE defines the scaling (shift) of the time_phase variable, + * which serves as a an extension to the low-order bits of the system + * clock variable time.tv_usec. + * + * SHIFT_UPDATE defines the scaling (shift) of the time_offset variable, + * which represents the current time offset with respect to standard + * time. + * + * SHIFT_USEC defines the scaling (shift) of the time_freq and + * time_tolerance variables, which represent the current frequency + * offset and maximum frequency tolerance. + * + * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable. + */ +#define SHIFT_SCALE 22 /* phase scale (shift) */ +#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */ +#define SHIFT_USEC 16 /* frequency offset scale (shift) */ +#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */ + +/* + * The following defines establish the performance envelope of the PLL. + * They insure it operates 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 is the maximum 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 + * PPS discipline code is configured (PPS_SYNC), the oscillator time and + * frequency are disciplined to an external source, presumably with + * negligible time and frequency error relative to UTC, and MAXFREQ can + * be reduced. + * + * MAXTIME is the maximum jitter tolerance of the PPS signal if the + * kernel PPS discipline code is configured (PPS_SYNC). + * + * MINSEC and MAXSEC define the lower and upper bounds on the interval + * between protocol updates. + */ +#define MAXPHASE 512000L /* max phase error (us) */ +#ifdef PPS_SYNC +#define MAXFREQ (512L << SHIFT_USEC) /* max freq error (100 ppm) */ +#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */ +#else +#define MAXFREQ (512L << SHIFT_USEC) /* max freq error (200 ppm) */ +#endif /* PPS_SYNC */ +#define MINSEC 16L /* min interval between updates (s) */ +#define MAXSEC 1200L /* max interval between updates (s) */ + +#ifdef PPS_SYNC +/* + * The following defines 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 Sun + * asynch driver. They establish the design parameters of the frequency- + * lock loop used to discipline the CPU clock oscillator to the PPS + * signal. + * + * PPS_AVG is the averaging factor for the frequency loop, as well as + * the time and frequency dispersion. + * + * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum + * calibration intervals, respectively, in seconds as a power of two. + * + * PPS_VALID is the maximum interval before the PPS signal is considered + * invalid and protocol updates used directly instead. + * + * MAXGLITCH is the maximum interval before a time offset of more than + * MAXTIME is believed. + */ +#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_VALID 120 /* pps signal watchdog max (s) */ +#define MAXGLITCH 30 /* pps signal glitch max (s) */ +#endif /* PPS_SYNC */ + +/* + * The following defines and structures define the user interface for + * the ntp_gettime() and ntp_adjtime() system calls. + * + * Control mode codes (timex.modes) + */ +#define MOD_OFFSET 0x0001 /* set time offset */ +#define MOD_FREQUENCY 0x0002 /* set frequency offset */ +#define MOD_MAXERROR 0x0004 /* set maximum time error */ +#define MOD_ESTERROR 0x0008 /* set estimated time error */ +#define MOD_STATUS 0x0010 /* set clock status bits */ +#define MOD_TIMECONST 0x0020 /* set pll time constant */ +#define MOD_CLKB 0x4000 /* set clock B */ +#define MOD_CLKA 0x8000 /* set clock A */ + +/* + * Status codes (timex.status) + */ +#define STA_PLL 0x0001 /* enable PLL updates (rw) */ +#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */ +#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */ +#define STA_FLL 0x0008 /* select frequency-lock mode (rw) */ + +#define STA_INS 0x0010 /* insert leap (rw) */ +#define STA_DEL 0x0020 /* delete leap (rw) */ +#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */ +#define STA_FREQHOLD 0x0080 /* hold frequency (rw) */ + +#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */ +#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */ +#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */ +#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */ + +#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */ + +#define STA_RONLY (STA_PPSSIGNAL | STA_PPSJITTER | STA_PPSWANDER | \ + STA_PPSERROR | STA_CLOCKERR) /* read-only bits */ + +/* + * Clock states (time_state) + */ +#define TIME_OK 0 /* no leap second warning */ +#define TIME_INS 1 /* insert leap second warning */ +#define TIME_DEL 2 /* delete leap second warning */ +#define TIME_OOP 3 /* leap second in progress */ +#define TIME_WAIT 4 /* leap second has occured */ +#define TIME_ERROR 5 /* clock not synchronized */ + +/* + * NTP user interface (ntp_gettime()) - used to read kernel clock values + * + * Note: maximum error = NTP synch distance = dispersion + delay / 2; + * estimated error = NTP dispersion. + */ +struct ntptimeval { + struct timeval time; /* current time (ro) */ + long maxerror; /* maximum error (us) (ro) */ + long esterror; /* estimated error (us) (ro) */ +}; + +/* + * NTP daemon interface - (ntp_adjtime()) used to discipline CPU clock + * oscillator + */ +struct timex { + unsigned int modes; /* clock mode bits (wo) */ + long offset; /* time offset (us) (rw) */ + long freq; /* frequency offset (scaled ppm) (rw) */ + long maxerror; /* maximum error (us) (rw) */ + long esterror; /* estimated error (us) (rw) */ + int status; /* clock status bits (rw) */ + long constant; /* pll time constant (rw) */ + long precision; /* clock precision (us) (ro) */ + long tolerance; /* clock frequency tolerance (scaled + * ppm) (ro) */ + /* + * The following read-only structure members are implemented + * only if the PPS signal discipline is configured in the + * kernel. + */ + long ppsfreq; /* pps frequency (scaled ppm) (ro) */ + long jitter; /* pps jitter (us) (ro) */ + int shift; /* interval duration (s) (shift) (ro) */ + long stabil; /* pps stability (scaled ppm) (ro) */ + long jitcnt; /* jitter limit exceeded (ro) */ + long calcnt; /* calibration intervals (ro) */ + long errcnt; /* calibration errors (ro) */ + long stbcnt; /* stability limit exceeded (ro) */ + +}; + +#ifndef _KERNEL +#include + +__BEGIN_DECLS +extern int ntp_gettime __P((struct ntptimeval *)); +extern int ntp_adjtime __P((struct timex *)); +__END_DECLS + +#endif /* not _KERNEL */ +#endif /* _SYS_TIMEX_H_ */ -- 2.20.1