Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_ntptime.c
Show First 20 Lines • Show All 156 Lines • ▼ Show 20 Lines | |||||
long time_esterror = MAXPHASE / 1000; /* estimated error (us) */ | long time_esterror = MAXPHASE / 1000; /* estimated error (us) */ | ||||
static long time_reftime; /* uptime at last adjustment (s) */ | static long time_reftime; /* uptime at last adjustment (s) */ | ||||
static l_fp time_offset; /* time offset (ns) */ | static l_fp time_offset; /* time offset (ns) */ | ||||
static l_fp time_freq; /* frequency offset (ns/s) */ | static l_fp time_freq; /* frequency offset (ns/s) */ | ||||
static l_fp time_adj; /* tick adjust (ns/s) */ | static l_fp time_adj; /* tick adjust (ns/s) */ | ||||
static int64_t time_adjtime; /* correction from adjtime(2) (usec) */ | static int64_t time_adjtime; /* correction from adjtime(2) (usec) */ | ||||
static struct mtx ntpadj_lock; | static struct mtx ntp_lock; | ||||
MTX_SYSINIT(ntpadj, &ntpadj_lock, "ntpadj", | MTX_SYSINIT(ntp, &ntp_lock, "ntp", MTX_SPIN); | ||||
#ifdef PPS_SYNC | |||||
MTX_SPIN | |||||
#else | |||||
MTX_DEF | |||||
#endif | |||||
); | |||||
/* | #define NTP_LOCK() mtx_lock_spin(&ntp_lock) | ||||
* When PPS_SYNC is defined, hardpps() function is provided which can | #define NTP_UNLOCK() mtx_unlock_spin(&ntp_lock) | ||||
* be legitimately called from interrupt filters. Due to this, use | #define NTP_ASSERT_LOCKED() mtx_assert(&ntp_lock, MA_OWNED) | ||||
* spinlock for ntptime state protection, otherwise sleepable mutex is | |||||
* adequate. | |||||
*/ | |||||
#ifdef PPS_SYNC | |||||
#define NTPADJ_LOCK() mtx_lock_spin(&ntpadj_lock) | |||||
#define NTPADJ_UNLOCK() mtx_unlock_spin(&ntpadj_lock) | |||||
#else | |||||
#define NTPADJ_LOCK() mtx_lock(&ntpadj_lock) | |||||
#define NTPADJ_UNLOCK() mtx_unlock(&ntpadj_lock) | |||||
#endif | |||||
#define NTPADJ_ASSERT_LOCKED() mtx_assert(&ntpadj_lock, MA_OWNED) | |||||
#ifdef PPS_SYNC | #ifdef PPS_SYNC | ||||
/* | /* | ||||
* The following variables are used when a pulse-per-second (PPS) signal | * The following variables are used when a pulse-per-second (PPS) signal | ||||
* is available and connected via a modem control lead. They establish | * is available and connected via a modem control lead. They establish | ||||
* the engineering parameters of the clock discipline loop when | * the engineering parameters of the clock discipline loop when | ||||
* controlled by the PPS signal. | * controlled by the PPS signal. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | ntp_is_time_error(int tsl) | ||||
return (false); | return (false); | ||||
} | } | ||||
static void | static void | ||||
ntp_gettime1(struct ntptimeval *ntvp) | ntp_gettime1(struct ntptimeval *ntvp) | ||||
{ | { | ||||
struct timespec atv; /* nanosecond time */ | struct timespec atv; /* nanosecond time */ | ||||
NTPADJ_ASSERT_LOCKED(); | NTP_ASSERT_LOCKED(); | ||||
nanotime(&atv); | nanotime(&atv); | ||||
ntvp->time.tv_sec = atv.tv_sec; | ntvp->time.tv_sec = atv.tv_sec; | ||||
ntvp->time.tv_nsec = atv.tv_nsec; | ntvp->time.tv_nsec = atv.tv_nsec; | ||||
ntvp->maxerror = time_maxerror; | ntvp->maxerror = time_maxerror; | ||||
ntvp->esterror = time_esterror; | ntvp->esterror = time_esterror; | ||||
ntvp->tai = time_tai; | ntvp->tai = time_tai; | ||||
ntvp->time_state = time_state; | ntvp->time_state = time_state; | ||||
Show All 14 Lines | |||||
}; | }; | ||||
#endif | #endif | ||||
/* ARGSUSED */ | /* ARGSUSED */ | ||||
int | int | ||||
sys_ntp_gettime(struct thread *td, struct ntp_gettime_args *uap) | sys_ntp_gettime(struct thread *td, struct ntp_gettime_args *uap) | ||||
{ | { | ||||
struct ntptimeval ntv; | struct ntptimeval ntv; | ||||
NTPADJ_LOCK(); | NTP_LOCK(); | ||||
ntp_gettime1(&ntv); | ntp_gettime1(&ntv); | ||||
NTPADJ_UNLOCK(); | NTP_UNLOCK(); | ||||
td->td_retval[0] = ntv.time_state; | td->td_retval[0] = ntv.time_state; | ||||
return (copyout(&ntv, uap->ntvp, sizeof(ntv))); | return (copyout(&ntv, uap->ntvp, sizeof(ntv))); | ||||
} | } | ||||
static int | static int | ||||
ntp_sysctl(SYSCTL_HANDLER_ARGS) | ntp_sysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
struct ntptimeval ntv; /* temporary structure */ | struct ntptimeval ntv; /* temporary structure */ | ||||
NTPADJ_LOCK(); | NTP_LOCK(); | ||||
ntp_gettime1(&ntv); | ntp_gettime1(&ntv); | ||||
NTPADJ_UNLOCK(); | NTP_UNLOCK(); | ||||
return (sysctl_handle_opaque(oidp, &ntv, sizeof(ntv), req)); | return (sysctl_handle_opaque(oidp, &ntv, sizeof(ntv), req)); | ||||
} | } | ||||
SYSCTL_NODE(_kern, OID_AUTO, ntp_pll, CTLFLAG_RW, 0, ""); | SYSCTL_NODE(_kern, OID_AUTO, ntp_pll, CTLFLAG_RW, 0, ""); | ||||
SYSCTL_PROC(_kern_ntp_pll, OID_AUTO, gettime, CTLTYPE_OPAQUE | CTLFLAG_RD | | SYSCTL_PROC(_kern_ntp_pll, OID_AUTO, gettime, CTLTYPE_OPAQUE | CTLFLAG_RD | | ||||
CTLFLAG_MPSAFE, 0, sizeof(struct ntptimeval) , ntp_sysctl, "S,ntptimeval", | CTLFLAG_MPSAFE, 0, sizeof(struct ntptimeval) , ntp_sysctl, "S,ntptimeval", | ||||
""); | ""); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | sys_ntp_adjtime(struct thread *td, struct ntp_adjtime_args *uap) | ||||
* the STA_PLL bit in the status word is cleared, the state and | * the STA_PLL bit in the status word is cleared, the state and | ||||
* status words are reset to the initial values at boot. | * status words are reset to the initial values at boot. | ||||
*/ | */ | ||||
modes = ntv.modes; | modes = ntv.modes; | ||||
if (modes) | if (modes) | ||||
error = priv_check(td, PRIV_NTP_ADJTIME); | error = priv_check(td, PRIV_NTP_ADJTIME); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
NTPADJ_LOCK(); | NTP_LOCK(); | ||||
if (modes & MOD_MAXERROR) | if (modes & MOD_MAXERROR) | ||||
time_maxerror = ntv.maxerror; | time_maxerror = ntv.maxerror; | ||||
if (modes & MOD_ESTERROR) | if (modes & MOD_ESTERROR) | ||||
time_esterror = ntv.esterror; | time_esterror = ntv.esterror; | ||||
if (modes & MOD_STATUS) { | if (modes & MOD_STATUS) { | ||||
if (time_status & STA_PLL && !(ntv.status & STA_PLL)) { | if (time_status & STA_PLL && !(ntv.status & STA_PLL)) { | ||||
time_state = TIME_OK; | time_state = TIME_OK; | ||||
time_status = STA_UNSYNC; | time_status = STA_UNSYNC; | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | else | ||||
ntv.jitter = pps_jitter / 1000; | ntv.jitter = pps_jitter / 1000; | ||||
ntv.stabil = pps_stabil; | ntv.stabil = pps_stabil; | ||||
ntv.calcnt = pps_calcnt; | ntv.calcnt = pps_calcnt; | ||||
ntv.errcnt = pps_errcnt; | ntv.errcnt = pps_errcnt; | ||||
ntv.jitcnt = pps_jitcnt; | ntv.jitcnt = pps_jitcnt; | ||||
ntv.stbcnt = pps_stbcnt; | ntv.stbcnt = pps_stbcnt; | ||||
#endif /* PPS_SYNC */ | #endif /* PPS_SYNC */ | ||||
retval = ntp_is_time_error(time_status) ? TIME_ERROR : time_state; | retval = ntp_is_time_error(time_status) ? TIME_ERROR : time_state; | ||||
NTPADJ_UNLOCK(); | NTP_UNLOCK(); | ||||
error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv)); | error = copyout((caddr_t)&ntv, (caddr_t)uap->tp, sizeof(ntv)); | ||||
if (error == 0) | if (error == 0) | ||||
td->td_retval[0] = retval; | td->td_retval[0] = retval; | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* second_overflow() - called after ntp_tick_adjust() | * second_overflow() - called after ntp_tick_adjust() | ||||
* | * | ||||
* This routine is ordinarily called immediately following the above | * This routine is ordinarily called immediately following the above | ||||
* routine ntp_tick_adjust(). While these two routines are normally | * routine ntp_tick_adjust(). While these two routines are normally | ||||
* combined, they are separated here only for the purposes of | * combined, they are separated here only for the purposes of | ||||
* simulation. | * simulation. | ||||
*/ | */ | ||||
void | void | ||||
ntp_update_second(int64_t *adjustment, time_t *newsec) | ntp_update_second(int64_t *adjustment, time_t *newsec) | ||||
{ | { | ||||
int tickrate; | int tickrate; | ||||
l_fp ftemp; /* 32/64-bit temporary */ | l_fp ftemp; /* 32/64-bit temporary */ | ||||
NTP_LOCK(); | |||||
/* | /* | ||||
* On rollover of the second both the nanosecond and microsecond | * On rollover of the second both the nanosecond and microsecond | ||||
* clocks are updated and the state machine cranked as | * clocks are updated and the state machine cranked as | ||||
* necessary. The phase adjustment to be used for the next | * necessary. The phase adjustment to be used for the next | ||||
* second is calculated and the maximum error is increased by | * second is calculated and the maximum error is increased by | ||||
* the tolerance. | * the tolerance. | ||||
*/ | */ | ||||
time_maxerror += MAXFREQ / 1000; | time_maxerror += MAXFREQ / 1000; | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | #endif /* PPS_SYNC */ | ||||
*adjustment = time_adj; | *adjustment = time_adj; | ||||
#ifdef PPS_SYNC | #ifdef PPS_SYNC | ||||
if (pps_valid > 0) | if (pps_valid > 0) | ||||
pps_valid--; | pps_valid--; | ||||
else | else | ||||
time_status &= ~STA_PPSSIGNAL; | time_status &= ~STA_PPSSIGNAL; | ||||
#endif /* PPS_SYNC */ | #endif /* PPS_SYNC */ | ||||
NTP_UNLOCK(); | |||||
} | } | ||||
/* | /* | ||||
* ntp_init() - initialize variables and structures | * ntp_init() - initialize variables and structures | ||||
* | * | ||||
* This routine must be called after the kernel variables hz and tick | * This routine must be called after the kernel variables hz and tick | ||||
* are set or changed and before the next tick interrupt. In this | * are set or changed and before the next tick interrupt. In this | ||||
* particular implementation, these values are assumed set elsewhere in | * particular implementation, these values are assumed set elsewhere in | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
hardupdate(offset) | hardupdate(offset) | ||||
long offset; /* clock offset (ns) */ | long offset; /* clock offset (ns) */ | ||||
{ | { | ||||
long mtemp; | long mtemp; | ||||
l_fp ftemp; | l_fp ftemp; | ||||
NTPADJ_ASSERT_LOCKED(); | NTP_ASSERT_LOCKED(); | ||||
/* | /* | ||||
* Select how the phase is to be controlled and from which | * Select how the phase is to be controlled and from which | ||||
* source. If the PPS signal is present and enabled to | * source. If the PPS signal is present and enabled to | ||||
* discipline the time, the PPS offset is used; otherwise, the | * discipline the time, the PPS offset is used; otherwise, the | ||||
* argument offset is used. | * argument offset is used. | ||||
*/ | */ | ||||
if (!(time_status & STA_PLL)) | if (!(time_status & STA_PLL)) | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
void | void | ||||
hardpps(tsp, nsec) | hardpps(tsp, nsec) | ||||
struct timespec *tsp; /* time at PPS */ | struct timespec *tsp; /* time at PPS */ | ||||
long nsec; /* hardware counter at PPS */ | long nsec; /* hardware counter at PPS */ | ||||
{ | { | ||||
long u_sec, u_nsec, v_nsec; /* temps */ | long u_sec, u_nsec, v_nsec; /* temps */ | ||||
l_fp ftemp; | l_fp ftemp; | ||||
NTPADJ_LOCK(); | NTP_LOCK(); | ||||
/* | /* | ||||
* The signal is first processed by a range gate and frequency | * The signal is first processed by a range gate and frequency | ||||
* discriminator. The range gate rejects noise spikes outside | * discriminator. The range gate rejects noise spikes outside | ||||
* the range +-500 us. The frequency discriminator rejects input | * the range +-500 us. The frequency discriminator rejects input | ||||
* signals with apparent frequency outside the range 1 +-500 | * signals with apparent frequency outside the range 1 +-500 | ||||
* PPM. If two hits occur in the same second, we ignore the | * PPM. If two hits occur in the same second, we ignore the | ||||
* later hit; if not and a hit occurs outside the range gate, | * later hit; if not and a hit occurs outside the range gate, | ||||
▲ Show 20 Lines • Show All 167 Lines • ▼ Show 20 Lines | hardpps(tsp, nsec) | ||||
if (u_nsec > MAXFREQ) | if (u_nsec > MAXFREQ) | ||||
L_LINT(pps_freq, MAXFREQ); | L_LINT(pps_freq, MAXFREQ); | ||||
else if (u_nsec < -MAXFREQ) | else if (u_nsec < -MAXFREQ) | ||||
L_LINT(pps_freq, -MAXFREQ); | L_LINT(pps_freq, -MAXFREQ); | ||||
if (time_status & STA_PPSFREQ) | if (time_status & STA_PPSFREQ) | ||||
time_freq = pps_freq; | time_freq = pps_freq; | ||||
out: | out: | ||||
NTPADJ_UNLOCK(); | NTP_UNLOCK(); | ||||
} | } | ||||
#endif /* PPS_SYNC */ | #endif /* PPS_SYNC */ | ||||
#ifndef _SYS_SYSPROTO_H_ | #ifndef _SYS_SYSPROTO_H_ | ||||
struct adjtime_args { | struct adjtime_args { | ||||
struct timeval *delta; | struct timeval *delta; | ||||
struct timeval *olddelta; | struct timeval *olddelta; | ||||
}; | }; | ||||
Show All 26 Lines | kern_adjtime(struct thread *td, struct timeval *delta, struct timeval *olddelta) | ||||
int error; | int error; | ||||
if (delta != NULL) { | if (delta != NULL) { | ||||
error = priv_check(td, PRIV_ADJTIME); | error = priv_check(td, PRIV_ADJTIME); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
ltw = (int64_t)delta->tv_sec * 1000000 + delta->tv_usec; | ltw = (int64_t)delta->tv_sec * 1000000 + delta->tv_usec; | ||||
} | } | ||||
NTPADJ_LOCK(); | NTP_LOCK(); | ||||
ltr = time_adjtime; | ltr = time_adjtime; | ||||
if (delta != NULL) | if (delta != NULL) | ||||
time_adjtime = ltw; | time_adjtime = ltw; | ||||
NTPADJ_UNLOCK(); | NTP_UNLOCK(); | ||||
if (olddelta != NULL) { | if (olddelta != NULL) { | ||||
atv.tv_sec = ltr / 1000000; | atv.tv_sec = ltr / 1000000; | ||||
atv.tv_usec = ltr % 1000000; | atv.tv_usec = ltr % 1000000; | ||||
if (atv.tv_usec < 0) { | if (atv.tv_usec < 0) { | ||||
atv.tv_usec += 1000000; | atv.tv_usec += 1000000; | ||||
atv.tv_sec--; | atv.tv_sec--; | ||||
} | } | ||||
*olddelta = atv; | *olddelta = atv; | ||||
▲ Show 20 Lines • Show All 69 Lines • Show Last 20 Lines |