diff options
Diffstat (limited to 'ntpd/ntp_loopfilter.c')
-rw-r--r-- | ntpd/ntp_loopfilter.c | 627 |
1 files changed, 410 insertions, 217 deletions
diff --git a/ntpd/ntp_loopfilter.c b/ntpd/ntp_loopfilter.c index 3eb09e10060a..87db726e417e 100644 --- a/ntpd/ntp_loopfilter.c +++ b/ntpd/ntp_loopfilter.c @@ -13,16 +13,13 @@ #include "ntp_unixtime.h" #include "ntp_stdlib.h" +#include <limits.h> #include <stdio.h> #include <ctype.h> #include <signal.h> #include <setjmp.h> -#if defined(VMS) && defined(VMS_LOCALUNIT) /*wjm*/ -#include "ntp_refclock.h" -#endif /* VMS */ - #ifdef KERNEL_PLL #include "ntp_syscall.h" #endif /* KERNEL_PLL */ @@ -35,15 +32,14 @@ * All units are in s and s/s, unless noted otherwise. */ #define CLOCK_MAX .128 /* default step threshold (s) */ -#define CLOCK_MINSTEP 900. /* default stepout threshold (s) */ +#define CLOCK_MINSTEP 300. /* default stepout threshold (s) */ #define CLOCK_PANIC 1000. /* default panic threshold (s) */ #define CLOCK_PHI 15e-6 /* max frequency error (s/s) */ #define CLOCK_PLL 16. /* PLL loop gain (log2) */ #define CLOCK_AVG 8. /* parameter averaging constant */ #define CLOCK_FLL .25 /* FLL loop gain */ +#define CLOCK_FLOOR .0005 /* startup offset floor (s) */ #define CLOCK_ALLAN 11 /* Allan intercept (log2 s) */ -#define CLOCK_DAY 86400. /* one day in seconds (s) */ -#define CLOCK_JUNE (CLOCK_DAY * 30) /* June in seconds (s) */ #define CLOCK_LIMIT 30 /* poll-adjust threshold */ #define CLOCK_PGATE 4. /* poll-adjust gate */ #define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */ @@ -119,13 +115,20 @@ u_char allan_xpt = CLOCK_ALLAN; /* Allan intercept (log2 s) */ static double clock_offset; /* offset */ double clock_jitter; /* offset jitter */ double drift_comp; /* frequency (s/s) */ +static double init_drift_comp; /* initial frequency (PPM) */ double clock_stability; /* frequency stability (wander) (s/s) */ double clock_codec; /* audio codec frequency (samples/s) */ static u_long clock_epoch; /* last update */ u_int sys_tai; /* TAI offset from UTC */ +static int loop_started; /* TRUE after LOOP_DRIFTINIT */ static void rstclock (int, double); /* transition function */ static double direct_freq(double); /* direct set frequency */ static void set_freq(double); /* set frequency */ +#ifndef PATH_MAX +# define PATH_MAX MAX_PATH +#endif +static char relative_path[PATH_MAX + 1]; /* relative path per recursive make */ +static char *this_file = NULL; #ifdef KERNEL_PLL static struct timex ntv; /* ntp_adjtime() parameters */ @@ -133,28 +136,31 @@ int pll_status; /* last kernel status bits */ #if defined(STA_NANO) && NTP_API == 4 static u_int loop_tai; /* last TAI offset */ #endif /* STA_NANO */ +static void start_kern_loop(void); +static void stop_kern_loop(void); #endif /* KERNEL_PLL */ /* * Clock state machine control flags */ -int ntp_enable = 1; /* clock discipline enabled */ +int ntp_enable = TRUE; /* clock discipline enabled */ int pll_control; /* kernel support available */ -int kern_enable = 1; /* kernel support enabled */ -int pps_enable; /* kernel PPS discipline enabled */ +int kern_enable = TRUE; /* kernel support enabled */ +int hardpps_enable; /* kernel PPS discipline enabled */ int ext_enable; /* external clock enabled */ int pps_stratum; /* pps stratum */ int allow_panic = FALSE; /* allow panic correction */ int mode_ntpdate = FALSE; /* exit on first clock set */ +int freq_cnt; /* initial frequency clamp */ +int freq_set; /* initial set frequency switch */ /* * Clock state machine variables */ -int state; /* clock discipline state */ +int state = 0; /* clock discipline state */ u_char sys_poll; /* time constant/poll (log2 s) */ int tc_counter; /* jiggle counter */ double last_offset; /* last offset (s) */ -static u_long last_step; /* last clock step */ /* * Huff-n'-puff filter variables @@ -177,6 +183,18 @@ static sigjmp_buf env; /* environment var. for pll_trap() */ #endif /* KERNEL_PLL */ /* + * file_name - return pointer to non-relative portion of this C file pathname + */ +static char *file_name(void) +{ + if (this_file == NULL) { + (void)strncpy(relative_path, __FILE__, PATH_MAX); + for (this_file=relative_path; *this_file && ! isalnum(*this_file); this_file++) ; + } + return this_file; +} + +/* * init_loopfilter - initialize loop filter data */ void @@ -187,7 +205,113 @@ init_loopfilter(void) */ sys_poll = ntp_minpoll; clock_jitter = LOGTOD(sys_precision); + freq_cnt = (int)clock_minstep; +} + +#ifdef KERNEL_PLL +/* + * ntp_adjtime_error_handler - process errors from ntp_adjtime + */ +static void +ntp_adjtime_error_handler( + const char *caller, /* name of calling function */ + struct timex *ptimex, /* pointer to struct timex */ + int ret, /* return value from ntp_adjtime */ + int saved_errno, /* value of errno when ntp_adjtime returned */ + int pps_call, /* ntp_adjtime call was PPS-related */ + int tai_call, /* ntp_adjtime call was TAI-related */ + int line /* line number of ntp_adjtime call */ + ) +{ + switch (ret) { + case -1: + switch (saved_errno) { + case EFAULT: + msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex pointer: 0x%lx", + caller, file_name(), line, + (long)((void *)ptimex) + ); + break; + case EINVAL: + msyslog(LOG_ERR, "%s: %s line %d: invalid struct timex \"constant\" element value: %ld", + caller, file_name(), line, + (long)(ptimex->constant) + ); + break; + case EPERM: + if (tai_call) { + errno = saved_errno; + msyslog(LOG_ERR, + "%s: ntp_adjtime(TAI) failed: %m", + caller); + } + errno = saved_errno; + msyslog(LOG_ERR, "%s: %s line %d: ntp_adjtime: %m", + caller, file_name(), line + ); + break; + default: + msyslog(LOG_NOTICE, "%s: %s line %d: unhandled errno value %d after failed ntp_adjtime call", + caller, file_name(), line, + saved_errno + ); + break; + } + break; +#ifdef TIME_OK + case TIME_OK: /* 0 no leap second warning */ + /* OK means OK */ + break; +#endif +#ifdef TIME_INS + case TIME_INS: /* 1 positive leap second warning */ + msyslog(LOG_INFO, "%s: %s line %d: kernel reports positive leap second warning state", + caller, file_name(), line + ); + break; +#endif +#ifdef TIME_DEL + case TIME_DEL: /* 2 negative leap second warning */ + msyslog(LOG_INFO, "%s: %s line %d: kernel reports negative leap second warning state", + caller, file_name(), line + ); + break; +#endif +#ifdef TIME_OOP + case TIME_OOP: /* 3 leap second in progress */ + msyslog(LOG_INFO, "%s: %s line %d: kernel reports leap second in progress", + caller, file_name(), line + ); + break; +#endif +#ifdef TIME_WAIT + case TIME_WAIT: /* 4 leap second has occured */ + msyslog(LOG_INFO, "%s: %s line %d: kernel reports leap second has occured", + caller, file_name(), line + ); + break; +#endif +#ifdef TIME_ERROR + case TIME_ERROR: /* loss of synchronization */ + if (pps_call && !(ptimex->status & STA_PPSSIGNAL)) + report_event(EVNT_KERN, NULL, + "PPS no signal"); + errno = saved_errno; + DPRINTF(1, ("kernel loop status (%s) %d %m\n", + k_st_flags(ptimex->status), errno)); + break; +#endif + default: + msyslog(LOG_NOTICE, "%s: %s line %d: unhandled return value %d from ntp_adjtime in %s at line %d", + caller, file_name(), line, + ret, + __func__, __LINE__ + ); + break; + } + return; } +#endif /* * local_clock - the NTP logical clock loop filter. @@ -209,6 +333,7 @@ local_clock( { int rval; /* return code */ int osys_poll; /* old system poll */ + int ntp_adj_ret; /* returned by ntp_adjtime */ double mu; /* interval since last update */ double clock_frequency; /* clock frequency */ double dtemp, etemp; /* double temps */ @@ -220,15 +345,16 @@ local_clock( * the open-loop response and then go home. */ #ifdef LOCKCLOCK - return (0); - -#else /* LOCKCLOCK */ + { +#else if (!ntp_enable) { +#endif /* LOCKCLOCK */ record_loop_stats(fp_offset, drift_comp, clock_jitter, clock_stability, sys_poll); return (0); } +#ifndef LOCKCLOCK /* * If the clock is way off, panic is declared. The clock_panic * defaults to 1000 s; if set to zero, the panic will never @@ -250,7 +376,7 @@ local_clock( /* * This section simulates ntpdate. If the offset exceeds the * step threshold (128 ms), step the clock to that time and - * exit. Othewise, slew the clock to that time and exit. Note + * exit. Otherwise, slew the clock to that time and exit. Note * that the slew will persist and eventually complete beyond the * life of this program. Note that while ntpdate is active, the * terminal does not detach, so the termination message prints @@ -260,7 +386,7 @@ local_clock( if (fabs(fp_offset) > clock_max && clock_max > 0) { step_systime(fp_offset); msyslog(LOG_NOTICE, "ntpd: time set %+.6f s", - fp_offset); + fp_offset); printf("ntpd: time set %+.6fs\n", fp_offset); } else { adj_systime(fp_offset); @@ -344,7 +470,7 @@ local_clock( clock_frequency = direct_freq(fp_offset); - /* fall through to S_SPIK */ + /* fall through to EVNT_SPIK */ /* * In SPIK state we ignore succeeding outlyers until @@ -389,12 +515,10 @@ local_clock( tc_counter = 0; clock_jitter = LOGTOD(sys_precision); rval = 2; - if (state == EVNT_NSET || (current_time - - last_step) < clock_minstep * 2) { + if (state == EVNT_NSET) { rstclock(EVNT_FREQ, 0); return (rval); } - last_step = current_time; break; } rstclock(EVNT_SYNC, 0); @@ -404,7 +528,7 @@ local_clock( * The offset is less than the step threshold. Calculate * the jitter as the exponentially weighted offset * differences. - */ + */ etemp = SQUARE(clock_jitter); dtemp = SQUARE(max(fabs(fp_offset - last_offset), LOGTOD(sys_precision))); @@ -419,66 +543,59 @@ local_clock( * the stepout threshold. */ case EVNT_NSET: + adj_systime(fp_offset); rstclock(EVNT_FREQ, fp_offset); break; /* - * In FSET state this is the first update received and - * the frequency has been initialized. Adjust the phase, - * but do not adjust the frequency until the next - * update. - */ - case EVNT_FSET: - rstclock(EVNT_SYNC, fp_offset); - break; - - /* * In FREQ state ignore updates until the stepout * threshold. After that, compute the new frequency, but - * do not adjust the phase or frequency until the next - * update. + * do not adjust the frequency until the holdoff counter + * decrements to zero. */ case EVNT_FREQ: if (mu < clock_minstep) return (0); clock_frequency = direct_freq(fp_offset); - rstclock(EVNT_SYNC, 0); - break; - + /* fall through */ /* - * We get here by default in SYNC and SPIK states. Here - * we compute the frequency update due to PLL and FLL - * contributions. + * We get here by default in FSET, SPIK and SYNC states. + * Here compute the frequency update due to PLL and FLL + * contributions. Note, we avoid frequency discipline at + * startup until the initial transient has subsided. */ default: allow_panic = FALSE; - - /* - * The FLL and PLL frequency gain constants - * depend on the time constant and Allan - * intercept. The PLL is always used, but - * becomes ineffective above the Allan intercept - * where the FLL becomes effective. - */ - if (sys_poll >= allan_xpt) - clock_frequency += (fp_offset - - clock_offset) / - max(ULOGTOD(sys_poll), mu) * - CLOCK_FLL; - - /* - * The PLL frequency gain (numerator) depends on - * the minimum of the update interval and Allan - * intercept. This reduces the PLL gain when the - * FLL becomes effective. - */ - etemp = min(ULOGTOD(allan_xpt), mu); - dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll); - clock_frequency += fp_offset * etemp / (dtemp * - dtemp); + if (freq_cnt == 0) { + + /* + * The FLL and PLL frequency gain constants + * depend on the time constant and Allan + * intercept. The PLL is always used, but + * becomes ineffective above the Allan intercept + * where the FLL becomes effective. + */ + if (sys_poll >= allan_xpt) + clock_frequency += (fp_offset - + clock_offset) / max(ULOGTOD(sys_poll), + mu) * CLOCK_FLL; + + /* + * The PLL frequency gain (numerator) depends on + * the minimum of the update interval and Allan + * intercept. This reduces the PLL gain when the + * FLL becomes effective. + */ + etemp = min(ULOGTOD(allan_xpt), mu); + dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll); + clock_frequency += fp_offset * etemp / (dtemp * + dtemp); + } rstclock(EVNT_SYNC, fp_offset); + if (fabs(fp_offset) < CLOCK_FLOOR) + freq_cnt = 0; break; } } @@ -499,7 +616,7 @@ local_clock( * lead to overflow problems. This might occur if some misguided * lad set the step threshold to something ridiculous. */ - if (pll_control && kern_enable) { + if (pll_control && kern_enable && freq_cnt == 0) { /* * We initialize the structure for the ntp_adjtime() @@ -512,7 +629,7 @@ local_clock( * frequency offsets for jitter and stability values and * to update the frequency file. */ - memset(&ntv, 0, sizeof(ntv)); + ZERO(ntv); if (ext_enable) { ntv.modes = MOD_STATUS; } else { @@ -534,6 +651,9 @@ local_clock( dtemp); ntv.constant = sys_poll - 4; #endif /* STA_NANO */ + if (ntv.constant < 0) + ntv.constant = 0; + ntv.esterror = (u_int32)(clock_jitter * 1e6); ntv.maxerror = (u_int32)((sys_rootdelay / 2 + sys_rootdisp) * 1e6); @@ -542,7 +662,7 @@ local_clock( /* * Enable/disable the PPS if requested. */ - if (pps_enable) { + if (hardpps_enable) { if (!(pll_status & STA_PPSTIME)) report_event(EVNT_KERN, NULL, "PPS enabled"); @@ -565,10 +685,8 @@ local_clock( * the pps. In any case, fetch the kernel offset, * frequency and jitter. */ - if (ntp_adjtime(&ntv) == TIME_ERROR) { - if (!(ntv.status & STA_PPSSIGNAL)) - report_event(EVNT_KERN, NULL, - "PPS no signal"); + if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) { + ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, hardpps_enable, 0, __LINE__ - 1); } pll_status = ntv.status; #ifdef STA_NANO @@ -597,7 +715,9 @@ local_clock( loop_tai = sys_tai; ntv.modes = MOD_TAI; ntv.constant = sys_tai; - ntp_adjtime(&ntv); + if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) { + ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 1, __LINE__ - 1); + } } #endif /* STA_NANO */ } @@ -626,16 +746,18 @@ local_clock( */ etemp = SQUARE(clock_stability); clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG); - drift_file_sw = TRUE; /* - * Here we adjust the timeconstan by comparing the current + * Here we adjust the time constant by comparing the current * offset with the clock jitter. If the offset is less than the * clock jitter times a constant, then the averaging interval is * increased, otherwise it is decreased. A bit of hysteresis - * helps calm the dance. Works best using burst mode. + * helps calm the dance. Works best using burst mode. Don't + * fiddle with the poll during the startup clamp period. */ - if (fabs(clock_offset) < CLOCK_PGATE * clock_jitter) { + if (freq_cnt > 0) { + tc_counter = 0; + } else if (fabs(clock_offset) < CLOCK_PGATE * clock_jitter) { tc_counter += sys_poll; if (tc_counter > CLOCK_LIMIT) { tc_counter = CLOCK_LIMIT; @@ -689,34 +811,68 @@ adj_host_clock( void ) { - double adjustment; + double offset_adj; + double freq_adj; /* * Update the dispersion since the last update. In contrast to * NTPv3, NTPv4 does not declare unsynchronized after one day, * since the dispersion check serves this function. Also, * since the poll interval can exceed one day, the old test - * would be counterproductive. + * would be counterproductive. During the startup clamp period, the + * time constant is clamped at 2. */ sys_rootdisp += clock_phi; - #ifndef LOCKCLOCK + if (!ntp_enable || mode_ntpdate) + return; /* - * If clock discipline is disabled or if the kernel is enabled, - * get out of Dodge quick. + * Determine the phase adjustment. The gain factor (denominator) + * increases with poll interval, so is dominated by the FLL + * above the Allan intercept. Note the reduced time constant at + * startup. */ - if (!ntp_enable || mode_ntpdate || (pll_control && - kern_enable)) - return; + if (state != EVNT_SYNC) { + offset_adj = 0.; + } else if (freq_cnt > 0) { + offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(1)); + freq_cnt--; +#ifdef KERNEL_PLL + } else if (pll_control && kern_enable) { + offset_adj = 0.; +#endif /* KERNEL_PLL */ + } else { + offset_adj = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll)); + } + + /* + * If the kernel discipline is enabled the frequency correction + * drift_comp has already been engaged via ntp_adjtime() in + * set_freq(). Otherwise it is a component of the adj_systime() + * offset. + */ +#ifdef KERNEL_PLL + if (pll_control && kern_enable) + freq_adj = 0.; + else +#endif /* KERNEL_PLL */ + freq_adj = drift_comp; + + /* Bound absolute value of total adjustment to NTP_MAXFREQ. */ + if (offset_adj + freq_adj > NTP_MAXFREQ) + offset_adj = NTP_MAXFREQ - freq_adj; + else if (offset_adj + freq_adj < -NTP_MAXFREQ) + offset_adj = -NTP_MAXFREQ - freq_adj; + clock_offset -= offset_adj; /* - * Implement the phase and frequency adjustments. The gain - * factor (denominator) increases with poll interval, so is - * dominated by the FLL above the Allan intercept. - */ - adjustment = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll)); - clock_offset -= adjustment; - adj_systime(adjustment + drift_comp); + * Windows port adj_systime() must be called each second, + * even if the argument is zero, to ease emulation of + * adjtime() using Windows' slew API which controls the rate + * but does not automatically stop slewing when an offset + * has decayed to zero. + */ + adj_systime(offset_adj + freq_adj); #endif /* LOCKCLOCK */ } @@ -743,6 +899,7 @@ rstclock( clock_epoch = current_time; } + /* * calc_freq - calculate frequency directly * @@ -759,71 +916,163 @@ direct_freq( double fp_offset ) { + set_freq(fp_offset / (current_time - clock_epoch)); -#ifdef KERNEL_PLL - /* - * If the kernel is enabled, we need the residual offset to - * calculate the frequency correction. - */ - if (pll_control && kern_enable) { - memset(&ntv, 0, sizeof(ntv)); - ntp_adjtime(&ntv); -#ifdef STA_NANO - clock_offset = ntv.offset / 1e9; -#else /* STA_NANO */ - clock_offset = ntv.offset / 1e6; -#endif /* STA_NANO */ - drift_comp = FREQTOD(ntv.freq); - } -#endif /* KERNEL_PLL */ - set_freq((fp_offset - clock_offset) / (current_time - - clock_epoch) + drift_comp); - wander_resid = 0; - return (drift_comp); + return drift_comp; } /* - * set_freq - set clock frequency + * set_freq - set clock frequency correction + * + * Used to step the frequency correction at startup, possibly again once + * the frequency is measured (that is, transitioning from EVNT_NSET to + * EVNT_FSET), and finally to switch between daemon and kernel loop + * discipline at runtime. + * + * When the kernel loop discipline is available but the daemon loop is + * in use, the kernel frequency correction is disabled (set to 0) to + * ensure drift_comp is applied by only one of the loops. */ static void set_freq( double freq /* frequency update */ ) { - char tbuf[80]; + const char * loop_desc; + int ntp_adj_ret; drift_comp = freq; + loop_desc = "ntpd"; +#ifdef KERNEL_PLL + if (pll_control) { + ZERO(ntv); + ntv.modes = MOD_FREQUENCY; + if (kern_enable) { + loop_desc = "kernel"; + ntv.freq = DTOFREQ(drift_comp); + } + if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) { + ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1); + } + } +#endif /* KERNEL_PLL */ + mprintf_event(EVNT_FSET, NULL, "%s %.3f PPM", loop_desc, + drift_comp * 1e6); +} + #ifdef KERNEL_PLL +static void +start_kern_loop(void) +{ + static int atexit_done; + int ntp_adj_ret; + + pll_control = TRUE; + ZERO(ntv); + ntv.modes = MOD_BITS; + ntv.status = STA_PLL; + ntv.maxerror = MAXDISPERSE; + ntv.esterror = MAXDISPERSE; + ntv.constant = sys_poll; /* why is it that here constant is unconditionally set to sys_poll, whereas elsewhere is is modified depending on nanosecond vs. microsecond kernel? */ +#ifdef SIGSYS /* - * If the kernel is enabled, update the kernel frequency. + * Use sigsetjmp() to save state and then call ntp_adjtime(); if + * it fails, then pll_trap() will set pll_control FALSE before + * returning control using siglogjmp(). */ - if (pll_control && kern_enable) { - memset(&ntv, 0, sizeof(ntv)); - ntv.modes = MOD_FREQUENCY; - ntv.freq = DTOFREQ(drift_comp); - ntp_adjtime(&ntv); - snprintf(tbuf, sizeof(tbuf), "kernel %.3f PPM", - drift_comp * 1e6); - report_event(EVNT_FSET, NULL, tbuf); + newsigsys.sa_handler = pll_trap; + newsigsys.sa_flags = 0; + if (sigaction(SIGSYS, &newsigsys, &sigsys)) { + msyslog(LOG_ERR, "sigaction() trap SIGSYS: %m"); + pll_control = FALSE; } else { - snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", - drift_comp * 1e6); - report_event(EVNT_FSET, NULL, tbuf); + if (sigsetjmp(env, 1) == 0) { + if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) { + ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1); + } + } + if (sigaction(SIGSYS, &sigsys, NULL)) { + msyslog(LOG_ERR, + "sigaction() restore SIGSYS: %m"); + pll_control = FALSE; + } } -#else /* KERNEL_PLL */ - snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", drift_comp * - 1e6); - report_event(EVNT_FSET, NULL, tbuf); -#endif /* KERNEL_PLL */ +#else /* SIGSYS */ + if ((ntp_adj_ret = ntp_adjtime(&ntv)) != 0) { + ntp_adjtime_error_handler(__func__, &ntv, ntp_adj_ret, errno, 0, 0, __LINE__ - 1); + } +#endif /* SIGSYS */ + + /* + * Save the result status and light up an external clock + * if available. + */ + pll_status = ntv.status; + if (pll_control) { + if (!atexit_done) { + atexit_done = TRUE; + atexit(&stop_kern_loop); + } +#ifdef STA_NANO + if (pll_status & STA_CLK) + ext_enable = TRUE; +#endif /* STA_NANO */ + report_event(EVNT_KERN, NULL, + "kernel time sync enabled"); + } +} +#endif /* KERNEL_PLL */ + + +#ifdef KERNEL_PLL +static void +stop_kern_loop(void) +{ + if (pll_control && kern_enable) + report_event(EVNT_KERN, NULL, + "kernel time sync disabled"); } +#endif /* KERNEL_PLL */ + + +/* + * select_loop() - choose kernel or daemon loop discipline. + */ +void +select_loop( + int use_kern_loop + ) +{ + if (kern_enable == use_kern_loop) + return; +#ifdef KERNEL_PLL + if (pll_control && !use_kern_loop) + stop_kern_loop(); +#endif + kern_enable = use_kern_loop; +#ifdef KERNEL_PLL + if (pll_control && use_kern_loop) + start_kern_loop(); +#endif + /* + * If this loop selection change occurs after initial startup, + * call set_freq() to switch the frequency compensation to or + * from the kernel loop. + */ +#ifdef KERNEL_PLL + if (pll_control && loop_started) + set_freq(drift_comp); +#endif +} + /* * huff-n'-puff filter */ void -huffpuff() +huffpuff(void) { int i; @@ -851,7 +1100,8 @@ loop_config( double freq ) { - int i; + int i; + double ftemp; #ifdef DEBUG if (debug > 1) @@ -870,92 +1120,28 @@ loop_config( if (mode_ntpdate) break; - pll_control = 1; - memset(&ntv, 0, sizeof(ntv)); - ntv.modes = MOD_BITS; - ntv.status = STA_PLL; - ntv.maxerror = MAXDISPERSE; - ntv.esterror = MAXDISPERSE; - ntv.constant = sys_poll; -#ifdef SIGSYS - /* - * Use sigsetjmp() to save state and then call - * ntp_adjtime(); if it fails, then siglongjmp() is used - * to return control - */ - newsigsys.sa_handler = pll_trap; - newsigsys.sa_flags = 0; - if (sigaction(SIGSYS, &newsigsys, &sigsys)) { - msyslog(LOG_ERR, - "sigaction() fails to save SIGSYS trap: %m"); - pll_control = 0; - } - if (sigsetjmp(env, 1) == 0) - ntp_adjtime(&ntv); - if ((sigaction(SIGSYS, &sigsys, - (struct sigaction *)NULL))) { - msyslog(LOG_ERR, - "sigaction() fails to restore SIGSYS trap: %m"); - pll_control = 0; - } -#else /* SIGSYS */ - ntp_adjtime(&ntv); -#endif /* SIGSYS */ + start_kern_loop(); +#endif /* KERNEL_PLL */ /* - * Save the result status and light up an external clock - * if available. + * Initialize frequency if given; otherwise, begin frequency + * calibration phase. */ - pll_status = ntv.status; - if (pll_control) { -#ifdef STA_NANO - if (pll_status & STA_CLK) - ext_enable = 1; -#endif /* STA_NANO */ - report_event(EVNT_KERN, NULL, - "kernel time sync enabled"); - } -#endif /* KERNEL_PLL */ -#endif /* LOCKCLOCK */ - break; - - /* - * Initialize the frequency. If the frequency file is missing or - * broken, set the initial frequency to zero and set the state - * to NSET. Otherwise, set the initial frequency to the given - * value and the state to FSET. - */ - case LOOP_DRIFTCOMP: -#ifndef LOCKCLOCK - if (freq > NTP_MAXFREQ || freq < -NTP_MAXFREQ) { - set_freq(0); - rstclock(EVNT_NSET, 0); - } else { - set_freq(freq); + ftemp = init_drift_comp / 1e6; + if (ftemp > NTP_MAXFREQ) + ftemp = NTP_MAXFREQ; + else if (ftemp < -NTP_MAXFREQ) + ftemp = -NTP_MAXFREQ; + set_freq(ftemp); + if (freq_set) rstclock(EVNT_FSET, 0); - } + else + rstclock(EVNT_NSET, 0); + loop_started = TRUE; #endif /* LOCKCLOCK */ break; - /* - * Disable the kernel at shutdown. The microkernel just abandons - * ship. The nanokernel carefully cleans up so applications can - * see this. Note the last programmed offset and frequency are - * left in place. - */ case LOOP_KERN_CLEAR: -#ifndef LOCKCLOCK -#ifdef KERNEL_PLL - if (pll_control && kern_enable) { - memset((char *)&ntv, 0, sizeof(ntv)); - ntv.modes = MOD_STATUS; - ntv.status = STA_UNSYNC; - ntp_adjtime(&ntv); - report_event(EVNT_KERN, NULL, - "kernel time sync disabledx"); - } -#endif /* KERNEL_PLL */ -#endif /* LOCKCLOCK */ break; /* @@ -974,15 +1160,15 @@ loop_config( break; case LOOP_FREQ: /* initial frequency (freq) */ - set_freq(freq / 1e6); - rstclock(EVNT_FSET, 0); + init_drift_comp = freq; + freq_set++; break; case LOOP_HUFFPUFF: /* huff-n'-puff length (huffpuff) */ if (freq < HUFFPUFF) freq = HUFFPUFF; sys_hufflen = (int)(freq / HUFFPUFF); - sys_huffpuff = (double *)emalloc(sizeof(double) * + sys_huffpuff = emalloc(sizeof(sys_huffpuff[0]) * sys_hufflen); for (i = 0; i < sys_hufflen; i++) sys_huffpuff[i] = 1e9; @@ -996,14 +1182,21 @@ loop_config( case LOOP_MAX: /* step threshold (step) */ clock_max = freq; if (clock_max == 0 || clock_max > 0.5) - kern_enable = 0; + select_loop(FALSE); break; case LOOP_MINSTEP: /* stepout threshold (stepout) */ - clock_minstep = freq; + if (freq < CLOCK_MINSTEP) + clock_minstep = CLOCK_MINSTEP; + else + clock_minstep = freq; + break; + + case LOOP_TICK: /* tick increment (tick) */ + set_sys_tick_precision(freq); break; - case LOOP_LEAP: /* not used */ + case LOOP_LEAP: /* not used, fall through */ default: msyslog(LOG_NOTICE, "loop_config: unsupported option %d", item); @@ -1025,7 +1218,7 @@ pll_trap( int arg ) { - pll_control = 0; + pll_control = FALSE; siglongjmp(env, 1); } #endif /* KERNEL_PLL && SIGSYS */ |