sys_netbsd: use timex driver
Remove the driver functions based on adjtime() and switch to the new timex driver, which is based on ntp_adjtime(). This allows chronyd to control the kernel frequency, adjust the offset with sub-microsecond accuracy, and set the kernel leap and sync status. A drawback is that the maximum slew rate is now limited by the 500 ppm maximum frequency offset, while adjtime() on NetBSD slewed by up to 5000 ppm.
This commit is contained in:
parent
1b2510e4b2
commit
d2d82e2e5f
2 changed files with 5 additions and 281 deletions
3
configure
vendored
3
configure
vendored
|
@ -419,8 +419,7 @@ case $SYSTEM in
|
||||||
echo "Configuring for $SYSTEM (using SunOS driver)"
|
echo "Configuring for $SYSTEM (using SunOS driver)"
|
||||||
;;
|
;;
|
||||||
NetBSD-* )
|
NetBSD-* )
|
||||||
EXTRA_OBJECTS="sys_netbsd.o"
|
EXTRA_OBJECTS="sys_generic.o sys_netbsd.o sys_timex.o"
|
||||||
EXTRA_LIBS="-lkvm"
|
|
||||||
try_clockctl=1
|
try_clockctl=1
|
||||||
add_def NETBSD
|
add_def NETBSD
|
||||||
echo "Configuring for $SYSTEM"
|
echo "Configuring for $SYSTEM"
|
||||||
|
|
283
sys_netbsd.c
283
sys_netbsd.c
|
@ -27,291 +27,18 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#ifdef NETBSD
|
#include "sysincl.h"
|
||||||
|
|
||||||
#include <kvm.h>
|
|
||||||
#include <nlist.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#include "sys_netbsd.h"
|
#include "sys_netbsd.h"
|
||||||
#include "localp.h"
|
#include "sys_timex.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
/* This register contains the number of seconds by which the local
|
|
||||||
clock was estimated to be fast of reference time at the epoch when
|
|
||||||
gettimeofday() returned T0 */
|
|
||||||
|
|
||||||
static double offset_register;
|
|
||||||
|
|
||||||
/* This register contains the epoch to which the offset is referenced */
|
|
||||||
|
|
||||||
static struct timeval T0;
|
|
||||||
|
|
||||||
/* This register contains the current estimate of the system
|
|
||||||
frequency, in absolute (NOT ppm) */
|
|
||||||
|
|
||||||
static double current_freq;
|
|
||||||
|
|
||||||
/* This register contains the number of seconds of adjustment that
|
|
||||||
were passed to adjtime last time it was called. */
|
|
||||||
|
|
||||||
static double adjustment_requested;
|
|
||||||
|
|
||||||
/* Kernel parameters to calculate adjtime error. */
|
|
||||||
|
|
||||||
static int kern_tickadj;
|
|
||||||
static long kern_bigadj;
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
clock_initialise(void)
|
|
||||||
{
|
|
||||||
struct timeval newadj, oldadj;
|
|
||||||
|
|
||||||
offset_register = 0.0;
|
|
||||||
adjustment_requested = 0.0;
|
|
||||||
current_freq = 0.0;
|
|
||||||
|
|
||||||
if (gettimeofday(&T0, NULL) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
newadj.tv_sec = 0;
|
|
||||||
newadj.tv_usec = 0;
|
|
||||||
|
|
||||||
if (adjtime(&newadj, &oldadj) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
clock_finalise(void)
|
|
||||||
{
|
|
||||||
/* Nothing to do yet */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
start_adjust(void)
|
|
||||||
{
|
|
||||||
struct timeval newadj, oldadj;
|
|
||||||
struct timeval T1;
|
|
||||||
double elapsed, accrued_error;
|
|
||||||
double adjust_required;
|
|
||||||
struct timeval exact_newadj;
|
|
||||||
long delta, tickdelta;
|
|
||||||
double rounding_error;
|
|
||||||
double old_adjust_remaining;
|
|
||||||
|
|
||||||
/* Determine the amount of error built up since the last adjustment */
|
|
||||||
if (gettimeofday(&T1, NULL) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
|
|
||||||
accrued_error = elapsed * current_freq;
|
|
||||||
|
|
||||||
adjust_required = - (accrued_error + offset_register);
|
|
||||||
|
|
||||||
UTI_DoubleToTimeval(adjust_required, &exact_newadj);
|
|
||||||
|
|
||||||
/* At this point, we need to round the required adjustment the
|
|
||||||
same way the kernel does. */
|
|
||||||
|
|
||||||
delta = exact_newadj.tv_sec * 1000000 + exact_newadj.tv_usec;
|
|
||||||
if (delta > kern_bigadj || delta < -kern_bigadj)
|
|
||||||
tickdelta = 10 * kern_tickadj;
|
|
||||||
else
|
|
||||||
tickdelta = kern_tickadj;
|
|
||||||
if (delta % tickdelta)
|
|
||||||
delta = delta / tickdelta * tickdelta;
|
|
||||||
newadj.tv_sec = 0;
|
|
||||||
newadj.tv_usec = delta;
|
|
||||||
UTI_NormaliseTimeval(&newadj);
|
|
||||||
|
|
||||||
/* Add rounding error back onto offset register. */
|
|
||||||
UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj);
|
|
||||||
|
|
||||||
if (adjtime(&newadj, &oldadj) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
|
|
||||||
|
|
||||||
offset_register = rounding_error - old_adjust_remaining;
|
|
||||||
|
|
||||||
T0 = T1;
|
|
||||||
UTI_TimevalToDouble(&newadj, &adjustment_requested);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
stop_adjust(void)
|
|
||||||
{
|
|
||||||
struct timeval T1;
|
|
||||||
struct timeval zeroadj, remadj;
|
|
||||||
double adjustment_remaining, adjustment_achieved;
|
|
||||||
double elapsed, elapsed_plus_adjust;
|
|
||||||
|
|
||||||
zeroadj.tv_sec = 0;
|
|
||||||
zeroadj.tv_usec = 0;
|
|
||||||
|
|
||||||
if (adjtime(&zeroadj, &remadj) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "adjtime() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gettimeofday(&T1, NULL) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
|
|
||||||
UTI_TimevalToDouble(&remadj, &adjustment_remaining);
|
|
||||||
|
|
||||||
adjustment_achieved = adjustment_requested - adjustment_remaining;
|
|
||||||
elapsed_plus_adjust = elapsed - adjustment_achieved;
|
|
||||||
|
|
||||||
offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
|
|
||||||
|
|
||||||
adjustment_requested = 0.0;
|
|
||||||
T0 = T1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
/* Positive offset means system clock is fast of true time, therefore
|
|
||||||
slew backwards */
|
|
||||||
|
|
||||||
static void
|
|
||||||
accrue_offset(double offset, double corr_rate)
|
|
||||||
{
|
|
||||||
stop_adjust();
|
|
||||||
offset_register += offset;
|
|
||||||
start_adjust();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
/* Positive offset means system clock is fast of true time, therefore
|
|
||||||
step backwards */
|
|
||||||
|
|
||||||
static int
|
|
||||||
apply_step_offset(double offset)
|
|
||||||
{
|
|
||||||
struct timeval old_time, new_time, T1;
|
|
||||||
|
|
||||||
stop_adjust();
|
|
||||||
|
|
||||||
if (gettimeofday(&old_time, NULL) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "gettimeofday() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
|
|
||||||
|
|
||||||
if (settimeofday(&new_time, NULL) < 0) {
|
|
||||||
DEBUG_LOG(LOGF_SysNetBSD, "settimeofday() failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
UTI_AddDoubleToTimeval(&T0, offset, &T1);
|
|
||||||
T0 = T1;
|
|
||||||
|
|
||||||
start_adjust();
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static double
|
|
||||||
set_frequency(double new_freq_ppm)
|
|
||||||
{
|
|
||||||
stop_adjust();
|
|
||||||
current_freq = new_freq_ppm * 1.0e-6;
|
|
||||||
start_adjust();
|
|
||||||
|
|
||||||
return current_freq * 1.0e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static double
|
|
||||||
read_frequency(void)
|
|
||||||
{
|
|
||||||
return current_freq * 1.0e6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
|
||||||
|
|
||||||
static void
|
|
||||||
get_offset_correction(struct timeval *raw,
|
|
||||||
double *corr, double *err)
|
|
||||||
{
|
|
||||||
stop_adjust();
|
|
||||||
*corr = -offset_register;
|
|
||||||
start_adjust();
|
|
||||||
if (err)
|
|
||||||
*err = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
SYS_NetBSD_Initialise(void)
|
SYS_NetBSD_Initialise(void)
|
||||||
{
|
{
|
||||||
static struct nlist nl[] = {
|
SYS_Timex_Initialise();
|
||||||
{"_tickadj"},
|
|
||||||
{"_bigadj"},
|
|
||||||
{NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
kvm_t *kt;
|
|
||||||
|
|
||||||
kt = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
|
|
||||||
if (!kt) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "Cannot open kvm");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kvm_nlist(kt, nl) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "Cannot read kernel symbols");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kvm_read(kt, nl[0].n_value, (char *)(&kern_tickadj), sizeof(int)) < 0) {
|
|
||||||
LOG_FATAL(LOGF_SysNetBSD, "Cannot read from _tickadj");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kvm_read(kt, nl[1].n_value, (char *)(&kern_bigadj), sizeof(long)) < 0) {
|
|
||||||
/* kernel doesn't have the symbol, use one second instead */
|
|
||||||
kern_bigadj = 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
kvm_close(kt);
|
|
||||||
|
|
||||||
clock_initialise();
|
|
||||||
|
|
||||||
lcl_RegisterSystemDrivers(read_frequency, set_frequency,
|
|
||||||
accrue_offset, apply_step_offset,
|
|
||||||
get_offset_correction,
|
|
||||||
NULL /* set_leap */,
|
|
||||||
NULL /* set_sync_status */);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
@ -319,7 +46,7 @@ SYS_NetBSD_Initialise(void)
|
||||||
void
|
void
|
||||||
SYS_NetBSD_Finalise(void)
|
SYS_NetBSD_Finalise(void)
|
||||||
{
|
{
|
||||||
clock_finalise();
|
SYS_Timex_Finalise();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
@ -348,5 +75,3 @@ SYS_NetBSD_DropRoot(uid_t uid, gid_t gid)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* NETBSD */
|
|
||||||
|
|
Loading…
Reference in a new issue