diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-10-29 15:22:58 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2013-10-31 13:18:53 +0100 |
commit | 89761ed7548cd45e9f01ade9e90ce9422ebfd99a (patch) | |
tree | 52e417de6e99951a599da4a5898de5ca1abdc107 /freebsd/sys | |
parent | selectpollkqueue01: New test (diff) | |
download | rtems-libbsd-89761ed7548cd45e9f01ade9e90ce9422ebfd99a.tar.bz2 |
Do not use FreeBSD time control
Diffstat (limited to 'freebsd/sys')
-rw-r--r-- | freebsd/sys/kern/kern_ntptime.c | 1045 | ||||
-rw-r--r-- | freebsd/sys/kern/kern_tc.c | 968 |
2 files changed, 0 insertions, 2013 deletions
diff --git a/freebsd/sys/kern/kern_ntptime.c b/freebsd/sys/kern/kern_ntptime.c deleted file mode 100644 index c49b82dd..00000000 --- a/freebsd/sys/kern/kern_ntptime.c +++ /dev/null @@ -1,1045 +0,0 @@ -#include <machine/rtems-bsd-config.h> - -/*- - *********************************************************************** - * * - * Copyright (c) David L. Mills 1993-2001 * - * * - * 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. * - * * - **********************************************************************/ - -/* - * Adapted from the original sources for FreeBSD and timecounters by: - * Poul-Henning Kamp <phk@FreeBSD.org>. - * - * The 32bit version of the "LP" macros seems a bit past its "sell by" - * date so I have retained only the 64bit version and included it directly - * in this file. - * - * Only minor changes done to interface with the timecounters over in - * sys/kern/kern_clock.c. Some of the comments below may be (even more) - * confusing and/or plain wrong in that context. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <rtems/bsd/local/opt_ntp.h> - -#include <rtems/bsd/sys/param.h> -#include <sys/systm.h> -#include <sys/sysproto.h> -#include <sys/eventhandler.h> -#include <sys/kernel.h> -#include <sys/priv.h> -#include <sys/proc.h> -#include <rtems/bsd/sys/lock.h> -#include <sys/mutex.h> -#include <rtems/bsd/sys/time.h> -#include <sys/timex.h> -#include <sys/timetc.h> -#include <sys/timepps.h> -#include <sys/syscallsubr.h> -#include <sys/sysctl.h> - -/* - * Single-precision macros for 64-bit machines - */ -typedef int64_t l_fp; -#define L_ADD(v, u) ((v) += (u)) -#define L_SUB(v, u) ((v) -= (u)) -#define L_ADDHI(v, a) ((v) += (int64_t)(a) << 32) -#define L_NEG(v) ((v) = -(v)) -#define L_RSHIFT(v, n) \ - do { \ - if ((v) < 0) \ - (v) = -(-(v) >> (n)); \ - else \ - (v) = (v) >> (n); \ - } while (0) -#define L_MPY(v, a) ((v) *= (a)) -#define L_CLR(v) ((v) = 0) -#define L_ISNEG(v) ((v) < 0) -#define L_LINT(v, a) ((v) = (int64_t)(a) << 32) -#define L_GINT(v) ((v) < 0 ? -(-(v) >> 32) : (v) >> 32) - -/* - * Generic NTP kernel interface - * - * These routines constitute 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 other routines in this module to adjust the - * phase and frequency of the clock discipline loop which controls the - * system clock. - * - * When the kernel time is reckoned directly in nanoseconds (NTP_NANO - * defined), the time at each tick interrupt is derived directly from - * the kernel time variable. When the kernel time is reckoned in - * microseconds, (NTP_NANO undefined), the time is derived from the - * kernel time variable together with a variable representing the - * leftover nanoseconds at the last tick interrupt. In either case, the - * current nanosecond time is reckoned from these values plus an - * interpolated value derived by the clock routines in another - * architecture-specific module. The interpolation can use either a - * dedicated counter or a processor cycle counter (PCC) implemented in - * some architectures. - * - * Note that all routines must run at priority splclock or higher. - */ -/* - * Phase/frequency-lock loop (PLL/FLL) definitions - * - * The nanosecond clock discipline uses two variable types, time - * variables and frequency variables. Both types are represented as 64- - * bit fixed-point quantities with the decimal point between two 32-bit - * halves. On a 32-bit machine, each half is represented as a single - * word and mathematical operations are done using multiple-precision - * arithmetic. On a 64-bit machine, ordinary computer arithmetic is - * used. - * - * A time variable is a signed 64-bit fixed-point number in ns and - * fraction. It represents the remaining time offset to be amortized - * over succeeding tick interrupts. The maximum time offset is about - * 0.5 s and the resolution is about 2.3e-10 ns. - * - * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |s s s| ns | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | fraction | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * - * A frequency variable is a signed 64-bit fixed-point number in ns/s - * and fraction. It represents the ns and fraction to be added to the - * kernel time variable at each second. The maximum frequency offset is - * about +-500000 ns/s and the resolution is about 2.3e-10 ns/s. - * - * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * |s s s s s s s s s s s s s| ns/s | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | fraction | - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ -/* - * The following variables establish the state of the PLL/FLL and the - * residual time and frequency offset of the local clock. - */ -#define SHIFT_PLL 4 /* PLL loop gain (shift) */ -#define SHIFT_FLL 2 /* FLL loop gain (shift) */ - -static int time_state = TIME_OK; /* clock state */ -static int time_status = STA_UNSYNC; /* clock status bits */ -static long time_tai; /* TAI offset (s) */ -static long time_monitor; /* last time offset scaled (ns) */ -static long time_constant; /* poll interval (shift) (s) */ -static long time_precision = 1; /* clock precision (ns) */ -static long time_maxerror = MAXPHASE / 1000; /* maximum error (us) */ -static long time_esterror = MAXPHASE / 1000; /* estimated error (us) */ -static long time_reftime; /* time at last adjustment (s) */ -static l_fp time_offset; /* time offset (ns) */ -static l_fp time_freq; /* frequency offset (ns/s) */ -static l_fp time_adj; /* tick adjust (ns/s) */ - -static int64_t time_adjtime; /* correction from adjtime(2) (usec) */ - -#ifdef PPS_SYNC -/* - * The following variables are used when a pulse-per-second (PPS) signal - * is available and connected via a modem control lead. They establish - * the engineering parameters of the clock discipline loop when - * controlled by the PPS signal. - */ -#define PPS_FAVG 2 /* min freq avg interval (s) (shift) */ -#define PPS_FAVGDEF 8 /* default freq avg int (s) (shift) */ -#define PPS_FAVGMAX 15 /* max freq avg interval (s) (shift) */ -#define PPS_PAVG 4 /* phase avg interval (s) (shift) */ -#define PPS_VALID 120 /* PPS signal watchdog max (s) */ -#define PPS_MAXWANDER 100000 /* max PPS wander (ns/s) */ -#define PPS_POPCORN 2 /* popcorn spike threshold (shift) */ - -static struct timespec pps_tf[3]; /* phase median filter */ -static l_fp pps_freq; /* scaled frequency offset (ns/s) */ -static long pps_fcount; /* frequency accumulator */ -static long pps_jitter; /* nominal jitter (ns) */ -static long pps_stabil; /* nominal stability (scaled ns/s) */ -static long pps_lastsec; /* time at last calibration (s) */ -static int pps_valid; /* signal watchdog counter */ -static int pps_shift = PPS_FAVG; /* interval duration (s) (shift) */ -static int pps_shiftmax = PPS_FAVGDEF; /* max interval duration (s) (shift) */ -static int pps_intcnt; /* wander counter */ - -/* - * PPS signal quality monitors - */ -static long pps_calcnt; /* calibration intervals */ -static long pps_jitcnt; /* jitter limit exceeded */ -static long pps_stbcnt; /* stability limit exceeded */ -static long pps_errcnt; /* calibration errors */ -#endif /* PPS_SYNC */ -/* - * End of phase/frequency-lock loop (PLL/FLL) definitions - */ - -static void ntp_init(void); -static void hardupdate(long offset); -static void ntp_gettime1(struct ntptimeval *ntvp); -static int ntp_is_time_error(void); - -#ifndef __rtems__ -static int -ntp_is_time_error(void) -{ - /* - * 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))) - return (1); - - return (0); -} - -static void -ntp_gettime1(struct ntptimeval *ntvp) -{ - struct timespec atv; /* nanosecond time */ - - GIANT_REQUIRED; - - nanotime(&atv); - ntvp->time.tv_sec = atv.tv_sec; - ntvp->time.tv_nsec = atv.tv_nsec; - ntvp->maxerror = time_maxerror; - ntvp->esterror = time_esterror; - ntvp->tai = time_tai; - ntvp->time_state = time_state; - - if (ntp_is_time_error()) - ntvp->time_state = TIME_ERROR; -} - -/* - * ntp_gettime() - NTP user application interface - * - * See the timex.h header file for synopsis and API description. Note that - * the TAI offset is returned in the ntvtimeval.tai structure member. - */ -#ifndef _SYS_SYSPROTO_H_ -struct ntp_gettime_args { - struct ntptimeval *ntvp; -}; -#endif -/* ARGSUSED */ -int -ntp_gettime(struct thread *td, struct ntp_gettime_args *uap) -{ - struct ntptimeval ntv; - - mtx_lock(&Giant); - ntp_gettime1(&ntv); - mtx_unlock(&Giant); - - td->td_retval[0] = ntv.time_state; - return (copyout(&ntv, uap->ntvp, sizeof(ntv))); -} - -static int -ntp_sysctl(SYSCTL_HANDLER_ARGS) -{ - struct ntptimeval ntv; /* temporary structure */ - - ntp_gettime1(&ntv); - - return (sysctl_handle_opaque(oidp, &ntv, sizeof(ntv), req)); -} - -SYSCTL_NODE(_kern, OID_AUTO, ntp_pll, CTLFLAG_RW, 0, ""); -SYSCTL_PROC(_kern_ntp_pll, OID_AUTO, gettime, CTLTYPE_OPAQUE|CTLFLAG_RD, - 0, sizeof(struct ntptimeval) , ntp_sysctl, "S,ntptimeval", ""); - -#ifdef PPS_SYNC -SYSCTL_INT(_kern_ntp_pll, OID_AUTO, pps_shiftmax, CTLFLAG_RW, &pps_shiftmax, 0, ""); -SYSCTL_INT(_kern_ntp_pll, OID_AUTO, pps_shift, CTLFLAG_RW, &pps_shift, 0, ""); -SYSCTL_INT(_kern_ntp_pll, OID_AUTO, time_monitor, CTLFLAG_RD, &time_monitor, 0, ""); - -SYSCTL_OPAQUE(_kern_ntp_pll, OID_AUTO, pps_freq, CTLFLAG_RD, &pps_freq, sizeof(pps_freq), "I", ""); -SYSCTL_OPAQUE(_kern_ntp_pll, OID_AUTO, time_freq, CTLFLAG_RD, &time_freq, sizeof(time_freq), "I", ""); -#endif - -/* - * ntp_adjtime() - NTP daemon application interface - * - * See the timex.h header file for synopsis and API description. Note that - * the timex.constant structure member has a dual purpose to set the time - * constant and to set the TAI offset. - */ -#ifndef _SYS_SYSPROTO_H_ -struct ntp_adjtime_args { - struct timex *tp; -}; -#endif - -int -ntp_adjtime(struct thread *td, struct ntp_adjtime_args *uap) -{ - struct timex ntv; /* temporary structure */ - long freq; /* frequency ns/s) */ - int modes; /* mode bits from structure */ - int s; /* caller priority */ - int error; - - error = copyin((caddr_t)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. - * Note that either the time constant or TAI offset are loaded - * from the ntv.constant member, depending on the mode bits. If - * the STA_PLL bit in the status word is cleared, the state and - * status words are reset to the initial values at boot. - */ - mtx_lock(&Giant); - modes = ntv.modes; - if (modes) - error = priv_check(td, PRIV_NTP_ADJTIME); - if (error) - goto done2; - s = splclock(); - if (modes & MOD_MAXERROR) - time_maxerror = ntv.maxerror; - if (modes & MOD_ESTERROR) - time_esterror = ntv.esterror; - if (modes & MOD_STATUS) { - if (time_status & STA_PLL && !(ntv.status & STA_PLL)) { - time_state = TIME_OK; - time_status = STA_UNSYNC; -#ifdef PPS_SYNC - pps_shift = PPS_FAVG; -#endif /* PPS_SYNC */ - } - time_status &= STA_RONLY; - time_status |= ntv.status & ~STA_RONLY; - } - if (modes & MOD_TIMECONST) { - if (ntv.constant < 0) - time_constant = 0; - else if (ntv.constant > MAXTC) - time_constant = MAXTC; - else - time_constant = ntv.constant; - } - if (modes & MOD_TAI) { - if (ntv.constant > 0) /* XXX zero & negative numbers ? */ - time_tai = ntv.constant; - } -#ifdef PPS_SYNC - if (modes & MOD_PPSMAX) { - if (ntv.shift < PPS_FAVG) - pps_shiftmax = PPS_FAVG; - else if (ntv.shift > PPS_FAVGMAX) - pps_shiftmax = PPS_FAVGMAX; - else - pps_shiftmax = ntv.shift; - } -#endif /* PPS_SYNC */ - if (modes & MOD_NANO) - time_status |= STA_NANO; - if (modes & MOD_MICRO) - time_status &= ~STA_NANO; - if (modes & MOD_CLKB) - time_status |= STA_CLK; - if (modes & MOD_CLKA) - time_status &= ~STA_CLK; - if (modes & MOD_FREQUENCY) { - freq = (ntv.freq * 1000LL) >> 16; - if (freq > MAXFREQ) - L_LINT(time_freq, MAXFREQ); - else if (freq < -MAXFREQ) - L_LINT(time_freq, -MAXFREQ); - else { - /* - * ntv.freq is [PPM * 2^16] = [us/s * 2^16] - * time_freq is [ns/s * 2^32] - */ - time_freq = ntv.freq * 1000LL * 65536LL; - } -#ifdef PPS_SYNC - pps_freq = time_freq; -#endif /* PPS_SYNC */ - } - if (modes & MOD_OFFSET) { - if (time_status & STA_NANO) - hardupdate(ntv.offset); - else - hardupdate(ntv.offset * 1000); - } - - /* - * Retrieve all clock variables. Note that the TAI offset is - * returned only by ntp_gettime(); - */ - if (time_status & STA_NANO) - ntv.offset = L_GINT(time_offset); - else - ntv.offset = L_GINT(time_offset) / 1000; /* XXX rounding ? */ - ntv.freq = L_GINT((time_freq / 1000LL) << 16); - ntv.maxerror = time_maxerror; - ntv.esterror = time_esterror; - ntv.status = time_status; - ntv.constant = time_constant; - if (time_status & STA_NANO) - ntv.precision = time_precision; - else - ntv.precision = time_precision / 1000; - ntv.tolerance = MAXFREQ * SCALE_PPM; -#ifdef PPS_SYNC - ntv.shift = pps_shift; - ntv.ppsfreq = L_GINT((pps_freq / 1000LL) << 16); - if (time_status & STA_NANO) - ntv.jitter = pps_jitter; - else - ntv.jitter = pps_jitter / 1000; - ntv.stabil = pps_stabil; - ntv.calcnt = pps_calcnt; - ntv.errcnt = pps_errcnt; - ntv.jitcnt = pps_jitcnt; - ntv.stbcnt = pps_stbcnt; -#endif /* PPS_SYNC */ - splx(s); - - error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv)); - if (error) - goto done2; - - if (ntp_is_time_error()) - td->td_retval[0] = TIME_ERROR; - else - td->td_retval[0] = time_state; - -done2: - mtx_unlock(&Giant); - return (error); -} -#endif /* __rtems__ */ - -/* - * second_overflow() - called after ntp_tick_adjust() - * - * This routine is ordinarily called immediately following the above - * routine ntp_tick_adjust(). While these two routines are normally - * combined, they are separated here only for the purposes of - * simulation. - */ -void -ntp_update_second(int64_t *adjustment, time_t *newsec) -{ - int tickrate; - l_fp ftemp; /* 32/64-bit temporary */ - - /* - * On rollover of the second both the nanosecond and microsecond - * clocks are updated and the state machine cranked as - * necessary. The phase adjustment to be used for the next - * second is calculated and the maximum error is increased by - * the tolerance. - */ - time_maxerror += MAXFREQ / 1000; - - /* - * 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 nano_time() routine or - * external clock driver will insure that reported time - * is always monotonic. - */ - switch (time_state) { - - /* - * No warning. - */ - case TIME_OK: - if (time_status & STA_INS) - time_state = TIME_INS; - else if (time_status & STA_DEL) - time_state = TIME_DEL; - break; - - /* - * Insert second 23:59:60 following second - * 23:59:59. - */ - case TIME_INS: - if (!(time_status & STA_INS)) - time_state = TIME_OK; - else if ((*newsec) % 86400 == 0) { - (*newsec)--; - time_state = TIME_OOP; - time_tai++; - } - break; - - /* - * Delete second 23:59:59. - */ - case TIME_DEL: - if (!(time_status & STA_DEL)) - time_state = TIME_OK; - else if (((*newsec) + 1) % 86400 == 0) { - (*newsec)++; - time_tai--; - time_state = TIME_WAIT; - } - break; - - /* - * Insert second in progress. - */ - case TIME_OOP: - time_state = TIME_WAIT; - break; - - /* - * Wait for status bits to clear. - */ - case TIME_WAIT: - if (!(time_status & (STA_INS | STA_DEL))) - time_state = TIME_OK; - } - - /* - * Compute the total time adjustment for the next second - * in ns. The offset is reduced by a factor depending on - * whether the PPS signal is operating. Note that the - * value is in effect scaled by the clock frequency, - * since the adjustment is added at each tick interrupt. - */ - ftemp = time_offset; -#ifdef PPS_SYNC - /* XXX even if PPS signal dies we should finish adjustment ? */ - if (time_status & STA_PPSTIME && time_status & - STA_PPSSIGNAL) - L_RSHIFT(ftemp, pps_shift); - else - L_RSHIFT(ftemp, SHIFT_PLL + time_constant); -#else - L_RSHIFT(ftemp, SHIFT_PLL + time_constant); -#endif /* PPS_SYNC */ - time_adj = ftemp; - L_SUB(time_offset, ftemp); - L_ADD(time_adj, time_freq); - - /* - * Apply any correction from adjtime(2). If more than one second - * off we slew at a rate of 5ms/s (5000 PPM) else 500us/s (500PPM) - * until the last second is slewed the final < 500 usecs. - */ - if (time_adjtime != 0) { - if (time_adjtime > 1000000) - tickrate = 5000; - else if (time_adjtime < -1000000) - tickrate = -5000; - else if (time_adjtime > 500) - tickrate = 500; - else if (time_adjtime < -500) - tickrate = -500; - else - tickrate = time_adjtime; - time_adjtime -= tickrate; - L_LINT(ftemp, tickrate * 1000); - L_ADD(time_adj, ftemp); - } - *adjustment = time_adj; - -#ifdef PPS_SYNC - if (pps_valid > 0) - pps_valid--; - else - time_status &= ~STA_PPSSIGNAL; -#endif /* PPS_SYNC */ -} - -#ifndef __rtems__ -/* - * ntp_init() - initialize variables and structures - * - * This routine must be called after the kernel variables hz and tick - * are set or changed and before the next tick interrupt. In this - * particular implementation, these values are assumed set elsewhere in - * the kernel. The design allows the clock frequency and tick interval - * to be changed while the system is running. So, this routine should - * probably be integrated with the code that does that. - */ -static void -ntp_init() -{ - - /* - * The following variables are initialized only at startup. Only - * those structures not cleared by the compiler need to be - * initialized, and these only in the simulator. In the actual - * kernel, any nonzero values here will quickly evaporate. - */ - L_CLR(time_offset); - L_CLR(time_freq); -#ifdef PPS_SYNC - pps_tf[0].tv_sec = pps_tf[0].tv_nsec = 0; - pps_tf[1].tv_sec = pps_tf[1].tv_nsec = 0; - pps_tf[2].tv_sec = pps_tf[2].tv_nsec = 0; - pps_fcount = 0; - L_CLR(pps_freq); -#endif /* PPS_SYNC */ -} - -SYSINIT(ntpclocks, SI_SUB_CLOCKS, SI_ORDER_MIDDLE, ntp_init, NULL); - -/* - * hardupdate() - local clock update - * - * This routine is called by ntp_adjtime() to update the local clock - * phase and frequency. The implementation is of an adaptive-parameter, - * hybrid phase/frequency-lock loop (PLL/FLL). The routine computes new - * time and frequency offset estimates for each call. 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 uncompensated quartz crystal oscillators and nominal update - * intervals less than 256 s, operation should be in phase-lock mode, - * where the loop is disciplined to phase. For update intervals greater - * than 1024 s, operation should be in frequency-lock mode, where the - * loop is disciplined to frequency. Between 256 s and 1024 s, the mode - * is selected by the STA_MODE status bit. - */ -static void -hardupdate(offset) - long offset; /* clock offset (ns) */ -{ - long mtemp; - l_fp ftemp; - - /* - * Select how the phase is to be controlled and from which - * source. If the PPS signal is present and enabled to - * discipline the time, the PPS offset is used; otherwise, the - * argument offset is used. - */ - if (!(time_status & STA_PLL)) - return; - if (!(time_status & STA_PPSTIME && time_status & - STA_PPSSIGNAL)) { - if (offset > MAXPHASE) - time_monitor = MAXPHASE; - else if (offset < -MAXPHASE) - time_monitor = -MAXPHASE; - else - time_monitor = offset; - L_LINT(time_offset, time_monitor); - } - - /* - * Select how the frequency is to be controlled and in which - * mode (PLL or FLL). If the PPS signal is present and enabled - * to discipline the frequency, the PPS frequency is used; - * otherwise, the argument offset is used to compute it. - */ - if (time_status & STA_PPSFREQ && time_status & STA_PPSSIGNAL) { - time_reftime = time_second; - return; - } - if (time_status & STA_FREQHOLD || time_reftime == 0) - time_reftime = time_second; - mtemp = time_second - time_reftime; - L_LINT(ftemp, time_monitor); - L_RSHIFT(ftemp, (SHIFT_PLL + 2 + time_constant) << 1); - L_MPY(ftemp, mtemp); - L_ADD(time_freq, ftemp); - time_status &= ~STA_MODE; - if (mtemp >= MINSEC && (time_status & STA_FLL || mtemp > - MAXSEC)) { - L_LINT(ftemp, (time_monitor << 4) / mtemp); - L_RSHIFT(ftemp, SHIFT_FLL + 4); - L_ADD(time_freq, ftemp); - time_status |= STA_MODE; - } - time_reftime = time_second; - if (L_GINT(time_freq) > MAXFREQ) - L_LINT(time_freq, MAXFREQ); - else if (L_GINT(time_freq) < -MAXFREQ) - L_LINT(time_freq, -MAXFREQ); -} - -#ifdef PPS_SYNC -/* - * hardpps() - discipline CPU clock oscillator to external PPS signal - * - * This routine is called at each PPS interrupt in order to discipline - * the CPU clock oscillator to the PPS signal. There are two independent - * first-order feedback loops, one for the phase, the other for the - * frequency. The phase loop measures and grooms the PPS phase offset - * and leaves it in a handy spot for the seconds overflow routine. The - * frequency loop averages successive PPS phase differences and - * calculates the PPS frequency offset, which is also processed by the - * seconds overflow routine. The code requires the caller to capture the - * time and architecture-dependent hardware counter values in - * nanoseconds at the on-time PPS signal transition. - * - * Note that, on some Unix systems this routine runs at an interrupt - * priority level higher than the timer interrupt routine hardclock(). - * Therefore, the variables used are distinct from the hardclock() - * variables, except for the actual time and frequency variables, which - * are determined by this routine and updated atomically. - */ -void -hardpps(tsp, nsec) - struct timespec *tsp; /* time at PPS */ - long nsec; /* hardware counter at PPS */ -{ - long u_sec, u_nsec, v_nsec; /* temps */ - l_fp ftemp; - - /* - * The signal is first processed by a range gate and frequency - * discriminator. The range gate rejects noise spikes outside - * the range +-500 us. The frequency discriminator rejects input - * signals with apparent frequency outside the range 1 +-500 - * PPM. If two hits occur in the same second, we ignore the - * later hit; if not and a hit occurs outside the range gate, - * keep the later hit for later comparison, but do not process - * it. - */ - time_status |= STA_PPSSIGNAL | STA_PPSJITTER; - time_status &= ~(STA_PPSWANDER | STA_PPSERROR); - pps_valid = PPS_VALID; - u_sec = tsp->tv_sec; - u_nsec = tsp->tv_nsec; - if (u_nsec >= (NANOSECOND >> 1)) { - u_nsec -= NANOSECOND; - u_sec++; - } - v_nsec = u_nsec - pps_tf[0].tv_nsec; - if (u_sec == pps_tf[0].tv_sec && v_nsec < NANOSECOND - - MAXFREQ) - return; - pps_tf[2] = pps_tf[1]; - pps_tf[1] = pps_tf[0]; - pps_tf[0].tv_sec = u_sec; - pps_tf[0].tv_nsec = u_nsec; - - /* - * Compute the difference between the current and previous - * counter values. If the difference exceeds 0.5 s, assume it - * has wrapped around, so correct 1.0 s. If the result exceeds - * the tick interval, the sample point has crossed a tick - * boundary during the last second, so correct the tick. Very - * intricate. - */ - u_nsec = nsec; - if (u_nsec > (NANOSECOND >> 1)) - u_nsec -= NANOSECOND; - else if (u_nsec < -(NANOSECOND >> 1)) - u_nsec += NANOSECOND; - pps_fcount += u_nsec; - if (v_nsec > MAXFREQ || v_nsec < -MAXFREQ) - return; - time_status &= ~STA_PPSJITTER; - - /* - * A three-stage median filter is used to help denoise the PPS - * time. The median sample becomes the time offset estimate; the - * difference between the other two samples becomes the time - * dispersion (jitter) estimate. - */ - if (pps_tf[0].tv_nsec > pps_tf[1].tv_nsec) { - if (pps_tf[1].tv_nsec > pps_tf[2].tv_nsec) { - v_nsec = pps_tf[1].tv_nsec; /* 0 1 2 */ - u_nsec = pps_tf[0].tv_nsec - pps_tf[2].tv_nsec; - } else if (pps_tf[2].tv_nsec > pps_tf[0].tv_nsec) { - v_nsec = pps_tf[0].tv_nsec; /* 2 0 1 */ - u_nsec = pps_tf[2].tv_nsec - pps_tf[1].tv_nsec; - } else { - v_nsec = pps_tf[2].tv_nsec; /* 0 2 1 */ - u_nsec = pps_tf[0].tv_nsec - pps_tf[1].tv_nsec; - } - } else { - if (pps_tf[1].tv_nsec < pps_tf[2].tv_nsec) { - v_nsec = pps_tf[1].tv_nsec; /* 2 1 0 */ - u_nsec = pps_tf[2].tv_nsec - pps_tf[0].tv_nsec; - } else if (pps_tf[2].tv_nsec < pps_tf[0].tv_nsec) { - v_nsec = pps_tf[0].tv_nsec; /* 1 0 2 */ - u_nsec = pps_tf[1].tv_nsec - pps_tf[2].tv_nsec; - } else { - v_nsec = pps_tf[2].tv_nsec; /* 1 2 0 */ - u_nsec = pps_tf[1].tv_nsec - pps_tf[0].tv_nsec; - } - } - - /* - * Nominal jitter is due to PPS signal noise and interrupt - * latency. If it exceeds the popcorn threshold, the sample is - * discarded. otherwise, if so enabled, the time offset is - * updated. We can tolerate a modest loss of data here without - * much degrading time accuracy. - */ - if (u_nsec > (pps_jitter << PPS_POPCORN)) { - time_status |= STA_PPSJITTER; - pps_jitcnt++; - } else if (time_status & STA_PPSTIME) { - time_monitor = -v_nsec; - L_LINT(time_offset, time_monitor); - } - pps_jitter += (u_nsec - pps_jitter) >> PPS_FAVG; - u_sec = pps_tf[0].tv_sec - pps_lastsec; - if (u_sec < (1 << pps_shift)) - return; - - /* - * At the end of the calibration interval the difference between - * the first and last counter values becomes the scaled - * frequency. It will later be divided by the length of the - * interval to determine the frequency update. If the frequency - * exceeds a sanity threshold, or if the actual calibration - * interval is not equal to the expected length, the data are - * discarded. We can tolerate a modest loss of data here without - * much degrading frequency accuracy. - */ - pps_calcnt++; - v_nsec = -pps_fcount; - pps_lastsec = pps_tf[0].tv_sec; - pps_fcount = 0; - u_nsec = MAXFREQ << pps_shift; - if (v_nsec > u_nsec || v_nsec < -u_nsec || u_sec != (1 << - pps_shift)) { - time_status |= STA_PPSERROR; - pps_errcnt++; - return; - } - - /* - * Here the raw frequency offset and wander (stability) is - * calculated. If the wander is less than the wander threshold - * for four consecutive averaging intervals, the interval is - * doubled; if it is greater than the threshold for four - * consecutive intervals, the interval is halved. The scaled - * frequency offset is converted to frequency offset. The - * stability metric is calculated as the average of recent - * frequency changes, but is used only for performance - * monitoring. - */ - L_LINT(ftemp, v_nsec); - L_RSHIFT(ftemp, pps_shift); - L_SUB(ftemp, pps_freq); - u_nsec = L_GINT(ftemp); - if (u_nsec > PPS_MAXWANDER) { - L_LINT(ftemp, PPS_MAXWANDER); - pps_intcnt--; - time_status |= STA_PPSWANDER; - pps_stbcnt++; - } else if (u_nsec < -PPS_MAXWANDER) { - L_LINT(ftemp, -PPS_MAXWANDER); - pps_intcnt--; - time_status |= STA_PPSWANDER; - pps_stbcnt++; - } else { - pps_intcnt++; - } - if (pps_intcnt >= 4) { - pps_intcnt = 4; - if (pps_shift < pps_shiftmax) { - pps_shift++; - pps_intcnt = 0; - } - } else if (pps_intcnt <= -4 || pps_shift > pps_shiftmax) { - pps_intcnt = -4; - if (pps_shift > PPS_FAVG) { - pps_shift--; - pps_intcnt = 0; - } - } - if (u_nsec < 0) - u_nsec = -u_nsec; - pps_stabil += (u_nsec * SCALE_PPM - pps_stabil) >> PPS_FAVG; - - /* - * The PPS frequency is recalculated and clamped to the maximum - * MAXFREQ. If enabled, the system clock frequency is updated as - * well. - */ - L_ADD(pps_freq, ftemp); - u_nsec = L_GINT(pps_freq); - if (u_nsec > MAXFREQ) - L_LINT(pps_freq, MAXFREQ); - else if (u_nsec < -MAXFREQ) - L_LINT(pps_freq, -MAXFREQ); - if (time_status & STA_PPSFREQ) - time_freq = pps_freq; -} -#endif /* PPS_SYNC */ - -#ifndef _SYS_SYSPROTO_H_ -struct adjtime_args { - struct timeval *delta; - struct timeval *olddelta; -}; -#endif -/* ARGSUSED */ -int -adjtime(struct thread *td, struct adjtime_args *uap) -{ - struct timeval delta, olddelta, *deltap; - int error; - - if (uap->delta) { - error = copyin(uap->delta, &delta, sizeof(delta)); - if (error) - return (error); - deltap = δ - } else - deltap = NULL; - error = kern_adjtime(td, deltap, &olddelta); - if (uap->olddelta && error == 0) - error = copyout(&olddelta, uap->olddelta, sizeof(olddelta)); - return (error); -} - -int -kern_adjtime(struct thread *td, struct timeval *delta, struct timeval *olddelta) -{ - struct timeval atv; - int error; - - mtx_lock(&Giant); - if (olddelta) { - atv.tv_sec = time_adjtime / 1000000; - atv.tv_usec = time_adjtime % 1000000; - if (atv.tv_usec < 0) { - atv.tv_usec += 1000000; - atv.tv_sec--; - } - *olddelta = atv; - } - if (delta) { - if ((error = priv_check(td, PRIV_ADJTIME))) { - mtx_unlock(&Giant); - return (error); - } - time_adjtime = (int64_t)delta->tv_sec * 1000000 + - delta->tv_usec; - } - mtx_unlock(&Giant); - return (0); -} - -static struct callout resettodr_callout; -static int resettodr_period = 1800; - -static void -periodic_resettodr(void *arg __unused) -{ - - if (!ntp_is_time_error()) { - mtx_lock(&Giant); - resettodr(); - mtx_unlock(&Giant); - } - if (resettodr_period > 0) - callout_schedule(&resettodr_callout, resettodr_period * hz); -} - -static void -shutdown_resettodr(void *arg __unused, int howto __unused) -{ - - callout_drain(&resettodr_callout); - if (resettodr_period > 0 && !ntp_is_time_error()) { - mtx_lock(&Giant); - resettodr(); - mtx_unlock(&Giant); - } -} - -static int -sysctl_resettodr_period(SYSCTL_HANDLER_ARGS) -{ - int error; - - error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req); - if (error || !req->newptr) - return (error); - if (resettodr_period == 0) - callout_stop(&resettodr_callout); - else - callout_reset(&resettodr_callout, resettodr_period * hz, - periodic_resettodr, NULL); - return (0); -} - -SYSCTL_PROC(_machdep, OID_AUTO, rtc_save_period, CTLTYPE_INT|CTLFLAG_RW, - &resettodr_period, 1800, sysctl_resettodr_period, "I", - "Save system time to RTC with this period (in seconds)"); -TUNABLE_INT("machdep.rtc_save_period", &resettodr_period); - -static void -start_periodic_resettodr(void *arg __unused) -{ - - EVENTHANDLER_REGISTER(shutdown_pre_sync, shutdown_resettodr, NULL, - SHUTDOWN_PRI_FIRST); - callout_init(&resettodr_callout, 1); - if (resettodr_period == 0) - return; - callout_reset(&resettodr_callout, resettodr_period * hz, - periodic_resettodr, NULL); -} - -SYSINIT(periodic_resettodr, SI_SUB_RUN_SCHEDULER, SI_ORDER_MIDDLE, - start_periodic_resettodr, NULL); -#endif /* __rtems__ */ diff --git a/freebsd/sys/kern/kern_tc.c b/freebsd/sys/kern/kern_tc.c deleted file mode 100644 index effbe4c5..00000000 --- a/freebsd/sys/kern/kern_tc.c +++ /dev/null @@ -1,968 +0,0 @@ -#include <machine/rtems-bsd-config.h> - -/*- - * ---------------------------------------------------------------------------- - * "THE BEER-WARE LICENSE" (Revision 42): - * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you - * can do whatever you want with this stuff. If we meet some day, and you think - * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp - * ---------------------------------------------------------------------------- - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <rtems/bsd/local/opt_ntp.h> - -#include <rtems/bsd/sys/param.h> -#include <sys/kernel.h> -#include <sys/sysctl.h> -#include <sys/syslog.h> -#include <sys/systm.h> -#include <sys/timepps.h> -#include <sys/timetc.h> -#include <sys/timex.h> - -/* - * A large step happens on boot. This constant detects such steps. - * It is relatively small so that ntp_update_second gets called enough - * in the typical 'missed a couple of seconds' case, but doesn't loop - * forever when the time step is large. - */ -#define LARGE_STEP 200 - -/* - * Implement a dummy timecounter which we can use until we get a real one - * in the air. This allows the console and other early stuff to use - * time services. - */ - -static u_int -dummy_get_timecount(struct timecounter *tc) -{ - static u_int now; - - return (++now); -} - -static struct timecounter dummy_timecounter = { - dummy_get_timecount, 0, ~0u, 1000000, "dummy", -1000000 -}; - -struct timehands { - /* These fields must be initialized by the driver. */ - struct timecounter *th_counter; - int64_t th_adjustment; - u_int64_t th_scale; - u_int th_offset_count; - struct bintime th_offset; - struct timeval th_microtime; - struct timespec th_nanotime; - /* Fields not to be copied in tc_windup start with th_generation. */ - volatile u_int th_generation; - struct timehands *th_next; -}; - -static struct timehands th0; -static struct timehands th9 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th0}; -static struct timehands th8 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th9}; -static struct timehands th7 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th8}; -static struct timehands th6 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th7}; -static struct timehands th5 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th6}; -static struct timehands th4 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th5}; -static struct timehands th3 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th4}; -static struct timehands th2 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th3}; -static struct timehands th1 = { NULL, 0, 0, 0, {0, 0}, {0, 0}, {0, 0}, 0, &th2}; -static struct timehands th0 = { - &dummy_timecounter, - 0, - (uint64_t)-1 / 1000000, - 0, - {1, 0}, - {0, 0}, - {0, 0}, - 1, - &th1 -}; - -static struct timehands *volatile timehands = &th0; -struct timecounter *timecounter = &dummy_timecounter; -static struct timecounter *timecounters = &dummy_timecounter; - -time_t time_second = 1; -time_t time_uptime = 1; - -static struct bintime boottimebin; -struct timeval boottime; -static int sysctl_kern_boottime(SYSCTL_HANDLER_ARGS); -SYSCTL_PROC(_kern, KERN_BOOTTIME, boottime, CTLTYPE_STRUCT|CTLFLAG_RD, - NULL, 0, sysctl_kern_boottime, "S,timeval", "System boottime"); - -SYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, ""); -SYSCTL_NODE(_kern_timecounter, OID_AUTO, tc, CTLFLAG_RW, 0, ""); - -static int timestepwarnings; -SYSCTL_INT(_kern_timecounter, OID_AUTO, stepwarnings, CTLFLAG_RW, - ×tepwarnings, 0, "Log time steps"); - -static void tc_windup(void); -static void cpu_tick_calibrate(int); - -static int -sysctl_kern_boottime(SYSCTL_HANDLER_ARGS) -{ -#ifdef SCTL_MASK32 - int tv[2]; - - if (req->flags & SCTL_MASK32) { - tv[0] = boottime.tv_sec; - tv[1] = boottime.tv_usec; - return SYSCTL_OUT(req, tv, sizeof(tv)); - } else -#endif - return SYSCTL_OUT(req, &boottime, sizeof(boottime)); -} - -static int -sysctl_kern_timecounter_get(SYSCTL_HANDLER_ARGS) -{ - u_int ncount; - struct timecounter *tc = arg1; - - ncount = tc->tc_get_timecount(tc); - return sysctl_handle_int(oidp, &ncount, 0, req); -} - -static int -sysctl_kern_timecounter_freq(SYSCTL_HANDLER_ARGS) -{ - u_int64_t freq; - struct timecounter *tc = arg1; - - freq = tc->tc_frequency; - return sysctl_handle_quad(oidp, &freq, 0, req); -} - -/* - * Return the difference between the timehands' counter value now and what - * was when we copied it to the timehands' offset_count. - */ -static __inline u_int -tc_delta(struct timehands *th) -{ - struct timecounter *tc; - - tc = th->th_counter; - return ((tc->tc_get_timecount(tc) - th->th_offset_count) & - tc->tc_counter_mask); -} - -/* - * Functions for reading the time. We have to loop until we are sure that - * the timehands that we operated on was not updated under our feet. See - * the comment in <sys/time.h> for a description of these 12 functions. - */ - -void -binuptime(struct bintime *bt) -{ - struct timehands *th; - u_int gen; - - do { - th = timehands; - gen = th->th_generation; - *bt = th->th_offset; - bintime_addx(bt, th->th_scale * tc_delta(th)); - } while (gen == 0 || gen != th->th_generation); -} - -void -nanouptime(struct timespec *tsp) -{ - struct bintime bt; - - binuptime(&bt); - bintime2timespec(&bt, tsp); -} - -void -microuptime(struct timeval *tvp) -{ - struct bintime bt; - - binuptime(&bt); - bintime2timeval(&bt, tvp); -} - -void -bintime(struct bintime *bt) -{ - - binuptime(bt); - bintime_add(bt, &boottimebin); -} - -void -nanotime(struct timespec *tsp) -{ - struct bintime bt; - - bintime(&bt); - bintime2timespec(&bt, tsp); -} - -void -microtime(struct timeval *tvp) -{ - struct bintime bt; - - bintime(&bt); - bintime2timeval(&bt, tvp); -} - -void -getbinuptime(struct bintime *bt) -{ - struct timehands *th; - u_int gen; - - do { - th = timehands; - gen = th->th_generation; - *bt = th->th_offset; - } while (gen == 0 || gen != th->th_generation); -} - -void -getnanouptime(struct timespec *tsp) -{ - struct timehands *th; - u_int gen; - - do { - th = timehands; - gen = th->th_generation; - bintime2timespec(&th->th_offset, tsp); - } while (gen == 0 || gen != th->th_generation); -} - -void -getmicrouptime(struct timeval *tvp) -{ - struct timehands *th; - u_int gen; - - do { - th = timehands; - gen = th->th_generation; - bintime2timeval(&th->th_offset, tvp); - } while (gen == 0 || gen != th->th_generation); -} - -void -getbintime(struct bintime *bt) -{ - struct timehands *th; - u_int gen; - - do { - th = timehands; - gen = th->th_generation; - *bt = th->th_offset; - } while (gen == 0 || gen != th->th_generation); - bintime_add(bt, &boottimebin); -} - -void -getnanotime(struct timespec *tsp) -{ - struct timehands *th; - u_int gen; - - do { - th = timehands; - gen = th->th_generation; - *tsp = th->th_nanotime; - } while (gen == 0 || gen != th->th_generation); -} - -void -getmicrotime(struct timeval *tvp) -{ - struct timehands *th; - u_int gen; - - do { - th = timehands; - gen = th->th_generation; - *tvp = th->th_microtime; - } while (gen == 0 || gen != th->th_generation); -} - -/* - * Initialize a new timecounter and possibly use it. - */ -void -tc_init(struct timecounter *tc) -{ - u_int u; - struct sysctl_oid *tc_root; - - u = tc->tc_frequency / tc->tc_counter_mask; - /* XXX: We need some margin here, 10% is a guess */ - u *= 11; - u /= 10; - if (u > hz && tc->tc_quality >= 0) { - tc->tc_quality = -2000; - if (bootverbose) { - printf("Timecounter \"%s\" frequency %ju Hz", - tc->tc_name, (uintmax_t)tc->tc_frequency); - printf(" -- Insufficient hz, needs at least %u\n", u); - } - } else if (tc->tc_quality >= 0 || bootverbose) { - printf("Timecounter \"%s\" frequency %ju Hz quality %d\n", - tc->tc_name, (uintmax_t)tc->tc_frequency, - tc->tc_quality); - } - - tc->tc_next = timecounters; - timecounters = tc; - /* - * Set up sysctl tree for this counter. - */ - tc_root = SYSCTL_ADD_NODE(NULL, - SYSCTL_STATIC_CHILDREN(_kern_timecounter_tc), OID_AUTO, tc->tc_name, - CTLFLAG_RW, 0, "timecounter description"); - SYSCTL_ADD_UINT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, - "mask", CTLFLAG_RD, &(tc->tc_counter_mask), 0, - "mask for implemented bits"); - SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, - "counter", CTLTYPE_UINT | CTLFLAG_RD, tc, sizeof(*tc), - sysctl_kern_timecounter_get, "IU", "current timecounter value"); - SYSCTL_ADD_PROC(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, - "frequency", CTLTYPE_QUAD | CTLFLAG_RD, tc, sizeof(*tc), - sysctl_kern_timecounter_freq, "QU", "timecounter frequency"); - SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(tc_root), OID_AUTO, - "quality", CTLFLAG_RD, &(tc->tc_quality), 0, - "goodness of time counter"); - /* - * Never automatically use a timecounter with negative quality. - * Even though we run on the dummy counter, switching here may be - * worse since this timecounter may not be monotonous. - */ - if (tc->tc_quality < 0) - return; - if (tc->tc_quality < timecounter->tc_quality) - return; - if (tc->tc_quality == timecounter->tc_quality && - tc->tc_frequency < timecounter->tc_frequency) - return; - (void)tc->tc_get_timecount(tc); - (void)tc->tc_get_timecount(tc); - timecounter = tc; -} - -/* Report the frequency of the current timecounter. */ -u_int64_t -tc_getfrequency(void) -{ - - return (timehands->th_counter->tc_frequency); -} - -/* - * Step our concept of UTC. This is done by modifying our estimate of - * when we booted. - * XXX: not locked. - */ -void -tc_setclock(struct timespec *ts) -{ - struct timespec tbef, taft; - struct bintime bt, bt2; - - cpu_tick_calibrate(1); - nanotime(&tbef); - timespec2bintime(ts, &bt); - binuptime(&bt2); - bintime_sub(&bt, &bt2); - bintime_add(&bt2, &boottimebin); - boottimebin = bt; - bintime2timeval(&bt, &boottime); - - /* XXX fiddle all the little crinkly bits around the fiords... */ - tc_windup(); - nanotime(&taft); - if (timestepwarnings) { - log(LOG_INFO, - "Time stepped from %jd.%09ld to %jd.%09ld (%jd.%09ld)\n", - (intmax_t)tbef.tv_sec, tbef.tv_nsec, - (intmax_t)taft.tv_sec, taft.tv_nsec, - (intmax_t)ts->tv_sec, ts->tv_nsec); - } - cpu_tick_calibrate(1); -} - -/* - * Initialize the next struct timehands in the ring and make - * it the active timehands. Along the way we might switch to a different - * timecounter and/or do seconds processing in NTP. Slightly magic. - */ -static void -tc_windup(void) -{ - struct bintime bt; - struct timehands *th, *tho; - u_int64_t scale; - u_int delta, ncount, ogen; - int i; - time_t t; - - /* - * Make the next timehands a copy of the current one, but do not - * overwrite the generation or next pointer. While we update - * the contents, the generation must be zero. - */ - tho = timehands; - th = tho->th_next; - ogen = th->th_generation; - th->th_generation = 0; - bcopy(tho, th, offsetof(struct timehands, th_generation)); - - /* - * Capture a timecounter delta on the current timecounter and if - * changing timecounters, a counter value from the new timecounter. - * Update the offset fields accordingly. - */ - delta = tc_delta(th); - if (th->th_counter != timecounter) - ncount = timecounter->tc_get_timecount(timecounter); - else - ncount = 0; - th->th_offset_count += delta; - th->th_offset_count &= th->th_counter->tc_counter_mask; - while (delta > th->th_counter->tc_frequency) { - /* Eat complete unadjusted seconds. */ - delta -= th->th_counter->tc_frequency; - th->th_offset.sec++; - } - if ((delta > th->th_counter->tc_frequency / 2) && - (th->th_scale * delta < ((uint64_t)1 << 63))) { - /* The product th_scale * delta just barely overflows. */ - th->th_offset.sec++; - } - bintime_addx(&th->th_offset, th->th_scale * delta); - - /* - * Hardware latching timecounters may not generate interrupts on - * PPS events, so instead we poll them. There is a finite risk that - * the hardware might capture a count which is later than the one we - * got above, and therefore possibly in the next NTP second which might - * have a different rate than the current NTP second. It doesn't - * matter in practice. - */ - if (tho->th_counter->tc_poll_pps) - tho->th_counter->tc_poll_pps(tho->th_counter); - - /* - * Deal with NTP second processing. The for loop normally - * iterates at most once, but in extreme situations it might - * keep NTP sane if timeouts are not run for several seconds. - * At boot, the time step can be large when the TOD hardware - * has been read, so on really large steps, we call - * ntp_update_second only twice. We need to call it twice in - * case we missed a leap second. - */ - bt = th->th_offset; - bintime_add(&bt, &boottimebin); - i = bt.sec - tho->th_microtime.tv_sec; - if (i > LARGE_STEP) - i = 2; - for (; i > 0; i--) { - t = bt.sec; - ntp_update_second(&th->th_adjustment, &bt.sec); - if (bt.sec != t) - boottimebin.sec += bt.sec - t; - } - /* Update the UTC timestamps used by the get*() functions. */ - /* XXX shouldn't do this here. Should force non-`get' versions. */ - bintime2timeval(&bt, &th->th_microtime); - bintime2timespec(&bt, &th->th_nanotime); - - /* Now is a good time to change timecounters. */ - if (th->th_counter != timecounter) { - th->th_counter = timecounter; - th->th_offset_count = ncount; - } - - /*- - * Recalculate the scaling factor. We want the number of 1/2^64 - * fractions of a second per period of the hardware counter, taking - * into account the th_adjustment factor which the NTP PLL/adjtime(2) - * processing provides us with. - * - * The th_adjustment is nanoseconds per second with 32 bit binary - * fraction and we want 64 bit binary fraction of second: - * - * x = a * 2^32 / 10^9 = a * 4.294967296 - * - * The range of th_adjustment is +/- 5000PPM so inside a 64bit int - * we can only multiply by about 850 without overflowing, that - * leaves no suitably precise fractions for multiply before divide. - * - * Divide before multiply with a fraction of 2199/512 results in a - * systematic undercompensation of 10PPM of th_adjustment. On a - * 5000PPM adjustment this is a 0.05PPM error. This is acceptable. - * - * We happily sacrifice the lowest of the 64 bits of our result - * to the goddess of code clarity. - * - */ - scale = (u_int64_t)1 << 63; - scale += (th->th_adjustment / 1024) * 2199; - scale /= th->th_counter->tc_frequency; - th->th_scale = scale * 2; - - /* - * Now that the struct timehands is again consistent, set the new - * generation number, making sure to not make it zero. - */ - if (++ogen == 0) - ogen = 1; - th->th_generation = ogen; - - /* Go live with the new struct timehands. */ - time_second = th->th_microtime.tv_sec; - time_uptime = th->th_offset.sec; - timehands = th; -} - -/* Report or change the active timecounter hardware. */ -static int -sysctl_kern_timecounter_hardware(SYSCTL_HANDLER_ARGS) -{ - char newname[32]; - struct timecounter *newtc, *tc; - int error; - - tc = timecounter; - strlcpy(newname, tc->tc_name, sizeof(newname)); - - error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req); - if (error != 0 || req->newptr == NULL || - strcmp(newname, tc->tc_name) == 0) - return (error); - for (newtc = timecounters; newtc != NULL; newtc = newtc->tc_next) { - if (strcmp(newname, newtc->tc_name) != 0) - continue; - - /* Warm up new timecounter. */ - (void)newtc->tc_get_timecount(newtc); - (void)newtc->tc_get_timecount(newtc); - - timecounter = newtc; - return (0); - } - return (EINVAL); -} - -SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW, - 0, 0, sysctl_kern_timecounter_hardware, "A", - "Timecounter hardware selected"); - - -/* Report or change the active timecounter hardware. */ -static int -sysctl_kern_timecounter_choice(SYSCTL_HANDLER_ARGS) -{ - char buf[32], *spc; - struct timecounter *tc; - int error; - - spc = ""; - error = 0; - for (tc = timecounters; error == 0 && tc != NULL; tc = tc->tc_next) { - sprintf(buf, "%s%s(%d)", - spc, tc->tc_name, tc->tc_quality); - error = SYSCTL_OUT(req, buf, strlen(buf)); - spc = " "; - } - return (error); -} - -SYSCTL_PROC(_kern_timecounter, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD, - 0, 0, sysctl_kern_timecounter_choice, "A", "Timecounter hardware detected"); - -/* - * RFC 2783 PPS-API implementation. - */ - -int -pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps) -{ - pps_params_t *app; - struct pps_fetch_args *fapi; -#ifdef PPS_SYNC - struct pps_kcbind_args *kapi; -#endif - - KASSERT(pps != NULL, ("NULL pps pointer in pps_ioctl")); - switch (cmd) { - case PPS_IOC_CREATE: - return (0); - case PPS_IOC_DESTROY: - return (0); - case PPS_IOC_SETPARAMS: - app = (pps_params_t *)data; - if (app->mode & ~pps->ppscap) - return (EINVAL); - pps->ppsparam = *app; - return (0); - case PPS_IOC_GETPARAMS: - app = (pps_params_t *)data; - *app = pps->ppsparam; - app->api_version = PPS_API_VERS_1; - return (0); - case PPS_IOC_GETCAP: - *(int*)data = pps->ppscap; - return (0); - case PPS_IOC_FETCH: - fapi = (struct pps_fetch_args *)data; - if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC) - return (EINVAL); - if (fapi->timeout.tv_sec || fapi->timeout.tv_nsec) - return (EOPNOTSUPP); - pps->ppsinfo.current_mode = pps->ppsparam.mode; - fapi->pps_info_buf = pps->ppsinfo; - return (0); - case PPS_IOC_KCBIND: -#ifdef PPS_SYNC - kapi = (struct pps_kcbind_args *)data; - /* XXX Only root should be able to do this */ - if (kapi->tsformat && kapi->tsformat != PPS_TSFMT_TSPEC) - return (EINVAL); - if (kapi->kernel_consumer != PPS_KC_HARDPPS) - return (EINVAL); - if (kapi->edge & ~pps->ppscap) - return (EINVAL); - pps->kcmode = kapi->edge; - return (0); -#else - return (EOPNOTSUPP); -#endif - default: - return (ENOIOCTL); - } -} - -void -pps_init(struct pps_state *pps) -{ - pps->ppscap |= PPS_TSFMT_TSPEC; - if (pps->ppscap & PPS_CAPTUREASSERT) - pps->ppscap |= PPS_OFFSETASSERT; - if (pps->ppscap & PPS_CAPTURECLEAR) - pps->ppscap |= PPS_OFFSETCLEAR; -} - -void -pps_capture(struct pps_state *pps) -{ - struct timehands *th; - - KASSERT(pps != NULL, ("NULL pps pointer in pps_capture")); - th = timehands; - pps->capgen = th->th_generation; - pps->capth = th; - pps->capcount = th->th_counter->tc_get_timecount(th->th_counter); - if (pps->capgen != th->th_generation) - pps->capgen = 0; -} - -void -pps_event(struct pps_state *pps, int event) -{ - struct bintime bt; - struct timespec ts, *tsp, *osp; - u_int tcount, *pcount; - int foff, fhard; - pps_seq_t *pseq; - - KASSERT(pps != NULL, ("NULL pps pointer in pps_event")); - /* If the timecounter was wound up underneath us, bail out. */ - if (pps->capgen == 0 || pps->capgen != pps->capth->th_generation) - return; - - /* Things would be easier with arrays. */ - if (event == PPS_CAPTUREASSERT) { - tsp = &pps->ppsinfo.assert_timestamp; - osp = &pps->ppsparam.assert_offset; - foff = pps->ppsparam.mode & PPS_OFFSETASSERT; - fhard = pps->kcmode & PPS_CAPTUREASSERT; - pcount = &pps->ppscount[0]; - pseq = &pps->ppsinfo.assert_sequence; - } else { - tsp = &pps->ppsinfo.clear_timestamp; - osp = &pps->ppsparam.clear_offset; - foff = pps->ppsparam.mode & PPS_OFFSETCLEAR; - fhard = pps->kcmode & PPS_CAPTURECLEAR; - pcount = &pps->ppscount[1]; - pseq = &pps->ppsinfo.clear_sequence; - } - - /* - * If the timecounter changed, we cannot compare the count values, so - * we have to drop the rest of the PPS-stuff until the next event. - */ - if (pps->ppstc != pps->capth->th_counter) { - pps->ppstc = pps->capth->th_counter; - *pcount = pps->capcount; - pps->ppscount[2] = pps->capcount; - return; - } - - /* Convert the count to a timespec. */ - tcount = pps->capcount - pps->capth->th_offset_count; - tcount &= pps->capth->th_counter->tc_counter_mask; - bt = pps->capth->th_offset; - bintime_addx(&bt, pps->capth->th_scale * tcount); - bintime_add(&bt, &boottimebin); - bintime2timespec(&bt, &ts); - - /* If the timecounter was wound up underneath us, bail out. */ - if (pps->capgen != pps->capth->th_generation) - return; - - *pcount = pps->capcount; - (*pseq)++; - *tsp = ts; - - if (foff) { - timespecadd(tsp, osp); - if (tsp->tv_nsec < 0) { - tsp->tv_nsec += 1000000000; - tsp->tv_sec -= 1; - } - } -#ifdef PPS_SYNC - if (fhard) { - u_int64_t scale; - - /* - * Feed the NTP PLL/FLL. - * The FLL wants to know how many (hardware) nanoseconds - * elapsed since the previous event. - */ - tcount = pps->capcount - pps->ppscount[2]; - pps->ppscount[2] = pps->capcount; - tcount &= pps->capth->th_counter->tc_counter_mask; - scale = (u_int64_t)1 << 63; - scale /= pps->capth->th_counter->tc_frequency; - scale *= 2; - bt.sec = 0; - bt.frac = 0; - bintime_addx(&bt, scale * tcount); - bintime2timespec(&bt, &ts); - hardpps(tsp, ts.tv_nsec + 1000000000 * ts.tv_sec); - } -#endif -} - -/* - * Timecounters need to be updated every so often to prevent the hardware - * counter from overflowing. Updating also recalculates the cached values - * used by the get*() family of functions, so their precision depends on - * the update frequency. - */ - -static int tc_tick; -SYSCTL_INT(_kern_timecounter, OID_AUTO, tick, CTLFLAG_RD, &tc_tick, 0, - "Approximate number of hardclock ticks in a millisecond"); - -void -tc_ticktock(void) -{ - static int count; - static time_t last_calib; - - if (++count < tc_tick) - return; - count = 0; - tc_windup(); - if (time_uptime != last_calib && !(time_uptime & 0xf)) { - cpu_tick_calibrate(0); - last_calib = time_uptime; - } -} - -static void -inittimecounter(void *dummy) -{ - u_int p; - - /* - * Set the initial timeout to - * max(1, <approx. number of hardclock ticks in a millisecond>). - * People should probably not use the sysctl to set the timeout - * to smaller than its inital value, since that value is the - * smallest reasonable one. If they want better timestamps they - * should use the non-"get"* functions. - */ - if (hz > 1000) - tc_tick = (hz + 500) / 1000; - else - tc_tick = 1; - p = (tc_tick * 1000000) / hz; - printf("Timecounters tick every %d.%03u msec\n", p / 1000, p % 1000); - - /* warm up new timecounter (again) and get rolling. */ - (void)timecounter->tc_get_timecount(timecounter); - (void)timecounter->tc_get_timecount(timecounter); -} - -SYSINIT(timecounter, SI_SUB_CLOCKS, SI_ORDER_SECOND, inittimecounter, NULL); - -/* Cpu tick handling -------------------------------------------------*/ - -static int cpu_tick_variable; -static uint64_t cpu_tick_frequency; - -static uint64_t -tc_cpu_ticks(void) -{ - static uint64_t base; - static unsigned last; - unsigned u; - struct timecounter *tc; - - tc = timehands->th_counter; - u = tc->tc_get_timecount(tc) & tc->tc_counter_mask; - if (u < last) - base += (uint64_t)tc->tc_counter_mask + 1; - last = u; - return (u + base); -} - -/* - * This function gets called every 16 seconds on only one designated - * CPU in the system from hardclock() via tc_ticktock(). - * - * Whenever the real time clock is stepped we get called with reset=1 - * to make sure we handle suspend/resume and similar events correctly. - */ - -static void -cpu_tick_calibrate(int reset) -{ - static uint64_t c_last; - uint64_t c_this, c_delta; - static struct bintime t_last; - struct bintime t_this, t_delta; - uint32_t divi; - - if (reset) { - /* The clock was stepped, abort & reset */ - t_last.sec = 0; - return; - } - - /* we don't calibrate fixed rate cputicks */ - if (!cpu_tick_variable) - return; - - getbinuptime(&t_this); - c_this = cpu_ticks(); - if (t_last.sec != 0) { - c_delta = c_this - c_last; - t_delta = t_this; - bintime_sub(&t_delta, &t_last); - /* - * Validate that 16 +/- 1/256 seconds passed. - * After division by 16 this gives us a precision of - * roughly 250PPM which is sufficient - */ - if (t_delta.sec > 16 || ( - t_delta.sec == 16 && t_delta.frac >= (0x01LL << 56))) { - /* too long */ - if (bootverbose) - printf("t_delta %ju.%016jx too long\n", - (uintmax_t)t_delta.sec, - (uintmax_t)t_delta.frac); - } else if (t_delta.sec < 15 || - (t_delta.sec == 15 && t_delta.frac <= (0xffLL << 56))) { - /* too short */ - if (bootverbose) - printf("t_delta %ju.%016jx too short\n", - (uintmax_t)t_delta.sec, - (uintmax_t)t_delta.frac); - } else { - /* just right */ - /* - * Headroom: - * 2^(64-20) / 16[s] = - * 2^(44) / 16[s] = - * 17.592.186.044.416 / 16 = - * 1.099.511.627.776 [Hz] - */ - divi = t_delta.sec << 20; - divi |= t_delta.frac >> (64 - 20); - c_delta <<= 20; - c_delta /= divi; - if (c_delta > cpu_tick_frequency) { - if (0 && bootverbose) - printf("cpu_tick increased to %ju Hz\n", - c_delta); - cpu_tick_frequency = c_delta; - } - } - } - c_last = c_this; - t_last = t_this; -} - -void -set_cputicker(cpu_tick_f *func, uint64_t freq, unsigned var) -{ - - if (func == NULL) { - cpu_ticks = tc_cpu_ticks; - } else { - cpu_tick_frequency = freq; - cpu_tick_variable = var; - cpu_ticks = func; - } -} - -uint64_t -cpu_tickrate(void) -{ - - if (cpu_ticks == tc_cpu_ticks) - return (tc_getfrequency()); - return (cpu_tick_frequency); -} - -/* - * We need to be slightly careful converting cputicks to microseconds. - * There is plenty of margin in 64 bits of microseconds (half a million - * years) and in 64 bits at 4 GHz (146 years), but if we do a multiply - * before divide conversion (to retain precision) we find that the - * margin shrinks to 1.5 hours (one millionth of 146y). - * With a three prong approach we never lose significant bits, no - * matter what the cputick rate and length of timeinterval is. - */ - -uint64_t -cputick2usec(uint64_t tick) -{ - - if (tick > 18446744073709551LL) /* floor(2^64 / 1000) */ - return (tick / (cpu_tickrate() / 1000000LL)); - else if (tick > 18446744073709LL) /* floor(2^64 / 1000000) */ - return ((tick * 1000LL) / (cpu_tickrate() / 1000LL)); - else - return ((tick * 1000000LL) / cpu_tickrate()); -} - -cpu_tick_f *cpu_ticks = tc_cpu_ticks; |