Introduce the sysclock_getsnapshot() and sysclock_snap2bintime() KPIs. The
sysclock_getsnapshot() function allows the caller to obtain a snapshot of all the system clock and timecounter state required to create time stamps at a later point. The sysclock_snap2bintime() function converts a previously obtained snapshot into a bintime time stamp according to the specified flags e.g. which system clock, uptime vs absolute time, etc. These KPIs enable useful functionality, including direct comparison of the feedback and feed-forward system clocks and generation of multiple time stamps with different formats from a single timecounter read. Committed on behalf of Julien Ridoux and Darryl Veitch from the University of Melbourne, Australia, as part of the FreeBSD Foundation funded "Feed-Forward Clock Synchronization Algorithms" project. For more information, see http://www.synclab.org/radclock/ In collaboration with: Julien Ridoux (jridoux at unimelb edu au)
This commit is contained in:
parent
da914858e1
commit
6cedd609b7
@ -148,13 +148,13 @@ typedef int64_t l_fp;
|
||||
#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 */
|
||||
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) */
|
||||
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) */
|
||||
|
@ -28,9 +28,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/systm.h>
|
||||
#ifdef FFCLOCK
|
||||
#include <sys/timeffc.h>
|
||||
#endif
|
||||
#include <sys/timepps.h>
|
||||
#include <sys/timetc.h>
|
||||
#include <sys/timex.h>
|
||||
@ -461,8 +459,6 @@ getmicrotime(struct timeval *tvp)
|
||||
* necessary.
|
||||
*/
|
||||
|
||||
int sysclock_active = SYSCLOCK_FBCK;
|
||||
|
||||
/* Feed-forward clock estimates kept updated by the synchronization daemon. */
|
||||
struct ffclock_estimate ffclock_estimate;
|
||||
struct bintime ffclock_boottime; /* Feed-forward boot time estimate. */
|
||||
@ -956,8 +952,148 @@ getmicrotime(struct timeval *tvp)
|
||||
|
||||
getmicrouptime_fromclock(tvp, sysclock_active);
|
||||
}
|
||||
|
||||
#endif /* FFCLOCK */
|
||||
|
||||
/*
|
||||
* System clock currently providing time to the system. Modifiable via sysctl
|
||||
* when the FFCLOCK option is defined.
|
||||
*/
|
||||
int sysclock_active = SYSCLOCK_FBCK;
|
||||
|
||||
/* Internal NTP status and error estimates. */
|
||||
extern int time_status;
|
||||
extern long time_esterror;
|
||||
|
||||
/*
|
||||
* Take a snapshot of sysclock data which can be used to compare system clocks
|
||||
* and generate timestamps after the fact.
|
||||
*/
|
||||
void
|
||||
sysclock_getsnapshot(struct sysclock_snap *clock_snap, int fast)
|
||||
{
|
||||
struct fbclock_info *fbi;
|
||||
struct timehands *th;
|
||||
struct bintime bt;
|
||||
unsigned int delta, gen;
|
||||
#ifdef FFCLOCK
|
||||
ffcounter ffcount;
|
||||
struct fftimehands *ffth;
|
||||
struct ffclock_info *ffi;
|
||||
struct ffclock_estimate cest;
|
||||
|
||||
ffi = &clock_snap->ff_info;
|
||||
#endif
|
||||
|
||||
fbi = &clock_snap->fb_info;
|
||||
delta = 0;
|
||||
|
||||
do {
|
||||
th = timehands;
|
||||
gen = th->th_generation;
|
||||
fbi->th_scale = th->th_scale;
|
||||
fbi->tick_time = th->th_offset;
|
||||
#ifdef FFCLOCK
|
||||
ffth = fftimehands;
|
||||
ffi->tick_time = ffth->tick_time_lerp;
|
||||
ffi->tick_time_lerp = ffth->tick_time_lerp;
|
||||
ffi->period = ffth->cest.period;
|
||||
ffi->period_lerp = ffth->period_lerp;
|
||||
clock_snap->ffcount = ffth->tick_ffcount;
|
||||
cest = ffth->cest;
|
||||
#endif
|
||||
if (!fast)
|
||||
delta = tc_delta(th);
|
||||
} while (gen == 0 || gen != th->th_generation);
|
||||
|
||||
clock_snap->delta = delta;
|
||||
clock_snap->sysclock_active = sysclock_active;
|
||||
|
||||
/* Record feedback clock status and error. */
|
||||
clock_snap->fb_info.status = time_status;
|
||||
/* XXX: Very crude estimate of feedback clock error. */
|
||||
bt.sec = time_esterror / 1000000;
|
||||
bt.frac = ((time_esterror - bt.sec) * 1000000) *
|
||||
(uint64_t)18446744073709ULL;
|
||||
clock_snap->fb_info.error = bt;
|
||||
|
||||
#ifdef FFCLOCK
|
||||
if (!fast)
|
||||
clock_snap->ffcount += delta;
|
||||
|
||||
/* Record feed-forward clock leap second adjustment. */
|
||||
ffi->leapsec_adjustment = cest.leapsec_total;
|
||||
if (clock_snap->ffcount > cest.leapsec_next)
|
||||
ffi->leapsec_adjustment -= cest.leapsec;
|
||||
|
||||
/* Record feed-forward clock status and error. */
|
||||
clock_snap->ff_info.status = cest.status;
|
||||
ffcount = clock_snap->ffcount - cest.update_ffcount;
|
||||
ffclock_convert_delta(ffcount, cest.period, &bt);
|
||||
/* 18446744073709 = int(2^64/1e12), err_bound_rate in [ps/s]. */
|
||||
bintime_mul(&bt, cest.errb_rate * (uint64_t)18446744073709ULL);
|
||||
/* 18446744073 = int(2^64 / 1e9), since err_abs in [ns]. */
|
||||
bintime_addx(&bt, cest.errb_abs * (uint64_t)18446744073ULL);
|
||||
clock_snap->ff_info.error = bt;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a sysclock snapshot into a struct bintime based on the specified
|
||||
* clock source and flags.
|
||||
*/
|
||||
int
|
||||
sysclock_snap2bintime(struct sysclock_snap *cs, struct bintime *bt,
|
||||
int whichclock, uint32_t flags)
|
||||
{
|
||||
#ifdef FFCLOCK
|
||||
struct bintime bt2;
|
||||
uint64_t period;
|
||||
#endif
|
||||
|
||||
switch (whichclock) {
|
||||
case SYSCLOCK_FBCK:
|
||||
*bt = cs->fb_info.tick_time;
|
||||
|
||||
/* If snapshot was created with !fast, delta will be >0. */
|
||||
if (cs->delta > 0)
|
||||
bintime_addx(bt, cs->fb_info.th_scale * cs->delta);
|
||||
|
||||
if ((flags & FBCLOCK_UPTIME) == 0)
|
||||
bintime_add(bt, &boottimebin);
|
||||
break;
|
||||
#ifdef FFCLOCK
|
||||
case SYSCLOCK_FFWD:
|
||||
if (flags & FFCLOCK_LERP) {
|
||||
*bt = cs->ff_info.tick_time_lerp;
|
||||
period = cs->ff_info.period_lerp;
|
||||
} else {
|
||||
*bt = cs->ff_info.tick_time;
|
||||
period = cs->ff_info.period;
|
||||
}
|
||||
|
||||
/* If snapshot was created with !fast, delta will be >0. */
|
||||
if (cs->delta > 0) {
|
||||
ffclock_convert_delta(cs->delta, period, &bt2);
|
||||
bintime_add(bt, &bt2);
|
||||
}
|
||||
|
||||
/* Leap second adjustment. */
|
||||
if (flags & FFCLOCK_LEAPSEC)
|
||||
bt->sec -= cs->ff_info.leapsec_adjustment;
|
||||
|
||||
/* Boot time adjustment, for uptime/monotonic clocks. */
|
||||
if (flags & FFCLOCK_UPTIME)
|
||||
bintime_sub(bt, &ffclock_boottime);
|
||||
#endif
|
||||
default:
|
||||
return (EINVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a new timecounter and possibly use it.
|
||||
*/
|
||||
|
@ -81,19 +81,75 @@ extern int sysclock_active;
|
||||
#define FFCLOCK_STA_WARMUP 2
|
||||
|
||||
/*
|
||||
* Clock flags to select how the feed-forward counter is converted to absolute
|
||||
* time by ffclock_convert_abs().
|
||||
* FAST: do not read the hardware counter, return feed-forward clock time
|
||||
* at last tick. The time returned has the resolution of the kernel
|
||||
* tick (1/hz [s]).
|
||||
* LERP: linear interpolation of ffclock time to guarantee monotonic time.
|
||||
* LEAPSEC: include leap seconds.
|
||||
* UPTIME: removes time of boot.
|
||||
* Flags for use by sysclock_snap2bintime() and various ffclock_ functions to
|
||||
* control how the timecounter hardware is read and how the hardware snapshot is
|
||||
* converted into absolute time.
|
||||
* {FB|FF}CLOCK_FAST: Do not read the hardware counter, instead using the
|
||||
* value at last tick. The time returned has a resolution
|
||||
* of the kernel tick timer (1/hz [s]).
|
||||
* FFCLOCK_LERP: Linear interpolation of ffclock time to guarantee
|
||||
* monotonic time.
|
||||
* FFCLOCK_LEAPSEC: Include leap seconds.
|
||||
* {FB|FF}CLOCK_UPTIME: Time stamp should be relative to system boot, not epoch.
|
||||
*/
|
||||
#define FFCLOCK_FAST 1
|
||||
#define FFCLOCK_LERP 2
|
||||
#define FFCLOCK_LEAPSEC 4
|
||||
#define FFCLOCK_UPTIME 8
|
||||
#define FFCLOCK_FAST 0x00000001
|
||||
#define FFCLOCK_LERP 0x00000002
|
||||
#define FFCLOCK_LEAPSEC 0x00000004
|
||||
#define FFCLOCK_UPTIME 0x00000008
|
||||
#define FFCLOCK_MASK 0x0000ffff
|
||||
|
||||
#define FBCLOCK_FAST 0x00010000 /* Currently unused. */
|
||||
#define FBCLOCK_UPTIME 0x00020000
|
||||
#define FBCLOCK_MASK 0xffff0000
|
||||
|
||||
/*
|
||||
* Feedback clock specific info structure. The feedback clock's estimation of
|
||||
* clock error is an absolute figure determined by the NTP algorithm. The status
|
||||
* is determined by the userland daemon.
|
||||
*/
|
||||
struct fbclock_info {
|
||||
struct bintime error;
|
||||
struct bintime tick_time;
|
||||
uint64_t th_scale;
|
||||
int status;
|
||||
};
|
||||
|
||||
/*
|
||||
* Feed-forward clock specific info structure. The feed-forward clock's
|
||||
* estimation of clock error is an upper bound, which although potentially
|
||||
* looser than the feedback clock equivalent, is much more reliable. The status
|
||||
* is determined by the userland daemon.
|
||||
*/
|
||||
struct ffclock_info {
|
||||
struct bintime error;
|
||||
struct bintime tick_time;
|
||||
struct bintime tick_time_lerp;
|
||||
uint64_t period;
|
||||
uint64_t period_lerp;
|
||||
int leapsec_adjustment;
|
||||
int status;
|
||||
};
|
||||
|
||||
/*
|
||||
* Snapshot of system clocks and related information. Holds time read from each
|
||||
* clock based on a single read of the active hardware timecounter, as well as
|
||||
* respective clock information such as error estimates and the ffcounter value
|
||||
* at the time of the read.
|
||||
*/
|
||||
struct sysclock_snap {
|
||||
struct fbclock_info fb_info;
|
||||
struct ffclock_info ff_info;
|
||||
ffcounter ffcount;
|
||||
unsigned int delta;
|
||||
int sysclock_active;
|
||||
};
|
||||
|
||||
/* Take a snapshot of the system clocks and related information. */
|
||||
void sysclock_getsnapshot(struct sysclock_snap *clock_snap, int fast);
|
||||
|
||||
/* Convert a timestamp from the selected system clock into bintime. */
|
||||
int sysclock_snap2bintime(struct sysclock_snap *cs, struct bintime *bt,
|
||||
int whichclock, uint32_t flags);
|
||||
|
||||
/* Resets feed-forward clock from RTC */
|
||||
void ffclock_reset_clock(struct timespec *ts);
|
||||
|
Loading…
Reference in New Issue
Block a user