1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-19 10:53:58 +00:00

- Provide a sysctl interface to change the active system clock at runtime.

- Wrap [get]{bin,nano,micro}[up]time() functions of sys/time.h to allow
  requesting time from either the feedback or the feed-forward clock. If a
  feedback (e.g. ntpd) and feed-forward (e.g. radclock) daemon are both running
  on the system, both kernel clocks are updated but only one serves time.

- Add similar wrappers for the feed-forward difference clock.

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/

Submitted by:	Julien Ridoux (jridoux at unimelb edu au)
This commit is contained in:
Lawrence Stewart 2011-11-20 05:32:12 +00:00
parent b3b514d6ac
commit 9bce0f05fe
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=227747
3 changed files with 568 additions and 2 deletions

View File

@ -31,6 +31,8 @@
__FBSDID("$FreeBSD$"); __FBSDID("$FreeBSD$");
#include <sys/param.h> #include <sys/param.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <sys/systm.h> #include <sys/systm.h>
#include <sys/timeffc.h> #include <sys/timeffc.h>
@ -127,3 +129,215 @@ ffclock_difftime(ffcounter ffdelta, struct bintime *bt,
bintime_mul(error_bound, err_rate * (uint64_t)18446744073709LL); bintime_mul(error_bound, err_rate * (uint64_t)18446744073709LL);
} }
} }
/*
* Sysctl for the Feed-Forward Clock.
*/
static int ffclock_version = 2;
SYSCTL_NODE(_kern, OID_AUTO, ffclock, CTLFLAG_RW, 0,
"Feed-Forward Clock Support");
SYSCTL_INT(_kern_ffclock, OID_AUTO, version, CTLFLAG_RD, &ffclock_version, 0,
"Version of Feed-Forward Clock Support");
/*
* Sysctl to select which clock is read when calling any of the
* [get]{bin,nano,micro}[up]time() functions.
*/
char *sysclocks[] = {"feedback", "feed-forward"};
#define NUM_SYSCLOCKS (sizeof(sysclocks) / sizeof(*sysclocks))
/* Report or change the active timecounter hardware. */
static int
sysctl_kern_ffclock_choice(SYSCTL_HANDLER_ARGS)
{
struct sbuf *s;
int clk, error;
s = sbuf_new_for_sysctl(NULL, NULL, 16 * NUM_SYSCLOCKS, req);
if (s == NULL)
return (ENOMEM);
for (clk = 0; clk < NUM_SYSCLOCKS; clk++) {
sbuf_cat(s, sysclocks[clk]);
if (clk + 1 < NUM_SYSCLOCKS)
sbuf_cat(s, " ");
}
error = sbuf_finish(s);
sbuf_delete(s);
return (error);
}
SYSCTL_PROC(_kern_ffclock, OID_AUTO, choice, CTLTYPE_STRING | CTLFLAG_RD,
0, 0, sysctl_kern_ffclock_choice, "A", "Clock paradigms available");
extern int sysclock_active;
static int
sysctl_kern_ffclock_active(SYSCTL_HANDLER_ARGS)
{
char newclock[32];
int error;
switch (sysclock_active) {
case SYSCLOCK_FBCK:
strlcpy(newclock, sysclocks[SYSCLOCK_FBCK], sizeof(newclock));
break;
case SYSCLOCK_FFWD:
strlcpy(newclock, sysclocks[SYSCLOCK_FFWD], sizeof(newclock));
break;
}
error = sysctl_handle_string(oidp, &newclock[0], sizeof(newclock), req);
if (error != 0 || req->newptr == NULL)
return (error);
if (strncmp(newclock, sysclocks[SYSCLOCK_FBCK],
sizeof(sysclocks[SYSCLOCK_FBCK])) == 0)
sysclock_active = SYSCLOCK_FBCK;
else if (strncmp(newclock, sysclocks[SYSCLOCK_FFWD],
sizeof(sysclocks[SYSCLOCK_FFWD])) == 0)
sysclock_active = SYSCLOCK_FFWD;
else
return (EINVAL);
return (error);
}
SYSCTL_PROC(_kern_ffclock, OID_AUTO, active, CTLTYPE_STRING | CTLFLAG_RW,
0, 0, sysctl_kern_ffclock_active, "A", "Kernel clock selected");
/*
* High level functions to access the Feed-Forward Clock.
*/
void
ffclock_bintime(struct bintime *bt)
{
ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
}
void
ffclock_nanotime(struct timespec *tsp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
bintime2timespec(&bt, tsp);
}
void
ffclock_microtime(struct timeval *tvp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_LEAPSEC);
bintime2timeval(&bt, tvp);
}
void
ffclock_getbintime(struct bintime *bt)
{
ffclock_abstime(NULL, bt, NULL,
FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
}
void
ffclock_getnanotime(struct timespec *tsp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL,
FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
bintime2timespec(&bt, tsp);
}
void
ffclock_getmicrotime(struct timeval *tvp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL,
FFCLOCK_LERP | FFCLOCK_LEAPSEC | FFCLOCK_FAST);
bintime2timeval(&bt, tvp);
}
void
ffclock_binuptime(struct bintime *bt)
{
ffclock_abstime(NULL, bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
}
void
ffclock_nanouptime(struct timespec *tsp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
bintime2timespec(&bt, tsp);
}
void
ffclock_microuptime(struct timeval *tvp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL, FFCLOCK_LERP | FFCLOCK_UPTIME);
bintime2timeval(&bt, tvp);
}
void
ffclock_getbinuptime(struct bintime *bt)
{
ffclock_abstime(NULL, bt, NULL,
FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
}
void
ffclock_getnanouptime(struct timespec *tsp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL,
FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
bintime2timespec(&bt, tsp);
}
void
ffclock_getmicrouptime(struct timeval *tvp)
{
struct bintime bt;
ffclock_abstime(NULL, &bt, NULL,
FFCLOCK_LERP | FFCLOCK_UPTIME | FFCLOCK_FAST);
bintime2timeval(&bt, tvp);
}
void
ffclock_bindifftime(ffcounter ffdelta, struct bintime *bt)
{
ffclock_difftime(ffdelta, bt, NULL);
}
void
ffclock_nanodifftime(ffcounter ffdelta, struct timespec *tsp)
{
struct bintime bt;
ffclock_difftime(ffdelta, &bt, NULL);
bintime2timespec(&bt, tsp);
}
void
ffclock_microdifftime(ffcounter ffdelta, struct timeval *tvp)
{
struct bintime bt;
ffclock_difftime(ffdelta, &bt, NULL);
bintime2timeval(&bt, tvp);
}

View File

@ -177,6 +177,144 @@ tc_delta(struct timehands *th)
* the comment in <sys/time.h> for a description of these 12 functions. * the comment in <sys/time.h> for a description of these 12 functions.
*/ */
#ifdef FFCLOCK
static void
fbclock_binuptime(struct bintime *bt)
{
struct timehands *th;
unsigned 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);
}
static void
fbclock_nanouptime(struct timespec *tsp)
{
struct bintime bt;
binuptime(&bt);
bintime2timespec(&bt, tsp);
}
static void
fbclock_microuptime(struct timeval *tvp)
{
struct bintime bt;
binuptime(&bt);
bintime2timeval(&bt, tvp);
}
static void
fbclock_bintime(struct bintime *bt)
{
binuptime(bt);
bintime_add(bt, &boottimebin);
}
static void
fbclock_nanotime(struct timespec *tsp)
{
struct bintime bt;
bintime(&bt);
bintime2timespec(&bt, tsp);
}
static void
fbclock_microtime(struct timeval *tvp)
{
struct bintime bt;
bintime(&bt);
bintime2timeval(&bt, tvp);
}
static void
fbclock_getbinuptime(struct bintime *bt)
{
struct timehands *th;
unsigned int gen;
do {
th = timehands;
gen = th->th_generation;
*bt = th->th_offset;
} while (gen == 0 || gen != th->th_generation);
}
static void
fbclock_getnanouptime(struct timespec *tsp)
{
struct timehands *th;
unsigned int gen;
do {
th = timehands;
gen = th->th_generation;
bintime2timespec(&th->th_offset, tsp);
} while (gen == 0 || gen != th->th_generation);
}
static void
fbclock_getmicrouptime(struct timeval *tvp)
{
struct timehands *th;
unsigned int gen;
do {
th = timehands;
gen = th->th_generation;
bintime2timeval(&th->th_offset, tvp);
} while (gen == 0 || gen != th->th_generation);
}
static void
fbclock_getbintime(struct bintime *bt)
{
struct timehands *th;
unsigned int gen;
do {
th = timehands;
gen = th->th_generation;
*bt = th->th_offset;
} while (gen == 0 || gen != th->th_generation);
bintime_add(bt, &boottimebin);
}
static void
fbclock_getnanotime(struct timespec *tsp)
{
struct timehands *th;
unsigned int gen;
do {
th = timehands;
gen = th->th_generation;
*tsp = th->th_nanotime;
} while (gen == 0 || gen != th->th_generation);
}
static void
fbclock_getmicrotime(struct timeval *tvp)
{
struct timehands *th;
unsigned int gen;
do {
th = timehands;
gen = th->th_generation;
*tvp = th->th_microtime;
} while (gen == 0 || gen != th->th_generation);
}
#else /* !FFCLOCK */
void void
binuptime(struct bintime *bt) binuptime(struct bintime *bt)
{ {
@ -313,6 +451,7 @@ getmicrotime(struct timeval *tvp)
*tvp = th->th_microtime; *tvp = th->th_microtime;
} while (gen == 0 || gen != th->th_generation); } while (gen == 0 || gen != th->th_generation);
} }
#endif /* FFCLOCK */
#ifdef FFCLOCK #ifdef FFCLOCK
/* /*
@ -322,6 +461,8 @@ getmicrotime(struct timeval *tvp)
* necessary. * necessary.
*/ */
int sysclock_active = SYSCLOCK_FBCK;
/* Feed-forward clock estimates kept updated by the synchronization daemon. */ /* Feed-forward clock estimates kept updated by the synchronization daemon. */
struct ffclock_estimate ffclock_estimate; struct ffclock_estimate ffclock_estimate;
struct bintime ffclock_boottime; /* Feed-forward boot time estimate. */ struct bintime ffclock_boottime; /* Feed-forward boot time estimate. */
@ -329,6 +470,38 @@ uint32_t ffclock_status; /* Feed-forward clock status. */
int8_t ffclock_updated; /* New estimates are available. */ int8_t ffclock_updated; /* New estimates are available. */
struct mtx ffclock_mtx; /* Mutex on ffclock_estimate. */ struct mtx ffclock_mtx; /* Mutex on ffclock_estimate. */
struct sysclock_ops {
int active;
void (*binuptime) (struct bintime *bt);
void (*nanouptime) (struct timespec *tsp);
void (*microuptime) (struct timeval *tvp);
void (*bintime) (struct bintime *bt);
void (*nanotime) (struct timespec *tsp);
void (*microtime) (struct timeval *tvp);
void (*getbinuptime) (struct bintime *bt);
void (*getnanouptime) (struct timespec *tsp);
void (*getmicrouptime) (struct timeval *tvp);
void (*getbintime) (struct bintime *bt);
void (*getnanotime) (struct timespec *tsp);
void (*getmicrotime) (struct timeval *tvp);
};
static struct sysclock_ops sysclock = {
.active = SYSCLOCK_FBCK,
.binuptime = fbclock_binuptime,
.nanouptime = fbclock_nanouptime,
.microuptime = fbclock_microuptime,
.bintime = fbclock_bintime,
.nanotime = fbclock_nanotime,
.microtime = fbclock_microtime,
.getbinuptime = fbclock_getbinuptime,
.getnanouptime = fbclock_getnanouptime,
.getmicrouptime = fbclock_getmicrouptime,
.getbintime = fbclock_getbintime,
.getnanotime = fbclock_getnanotime,
.getmicrotime = fbclock_getmicrotime
};
struct fftimehands { struct fftimehands {
struct ffclock_estimate cest; struct ffclock_estimate cest;
struct bintime tick_time; struct bintime tick_time;
@ -621,6 +794,46 @@ ffclock_change_tc(struct timehands *th)
fftimehands = ffth; fftimehands = ffth;
} }
static void
change_sysclock(int new_sysclock)
{
sysclock.active = new_sysclock;
switch (sysclock.active) {
case SYSCLOCK_FBCK:
sysclock.binuptime = fbclock_binuptime;
sysclock.nanouptime = fbclock_nanouptime;
sysclock.microuptime = fbclock_microuptime;
sysclock.bintime = fbclock_bintime;
sysclock.nanotime = fbclock_nanotime;
sysclock.microtime = fbclock_microtime;
sysclock.getbinuptime = fbclock_getbinuptime;
sysclock.getnanouptime = fbclock_getnanouptime;
sysclock.getmicrouptime = fbclock_getmicrouptime;
sysclock.getbintime = fbclock_getbintime;
sysclock.getnanotime = fbclock_getnanotime;
sysclock.getmicrotime = fbclock_getmicrotime;
break;
case SYSCLOCK_FFWD:
sysclock.binuptime = ffclock_binuptime;
sysclock.nanouptime = ffclock_nanouptime;
sysclock.microuptime = ffclock_microuptime;
sysclock.bintime = ffclock_bintime;
sysclock.nanotime = ffclock_nanotime;
sysclock.microtime = ffclock_microtime;
sysclock.getbinuptime = ffclock_getbinuptime;
sysclock.getnanouptime = ffclock_getnanouptime;
sysclock.getmicrouptime = ffclock_getmicrouptime;
sysclock.getbintime = ffclock_getbintime;
sysclock.getnanotime = ffclock_getnanotime;
sysclock.getmicrotime = ffclock_getmicrotime;
break;
default:
break;
}
}
/* /*
* Retrieve feed-forward counter and time of last kernel tick. * Retrieve feed-forward counter and time of last kernel tick.
*/ */
@ -731,6 +944,90 @@ ffclock_read_counter(ffcounter *ffcount)
*ffcount += delta; *ffcount += delta;
} }
void
binuptime(struct bintime *bt)
{
sysclock.binuptime(bt);
}
void
nanouptime(struct timespec *tsp)
{
sysclock.nanouptime(tsp);
}
void
microuptime(struct timeval *tvp)
{
sysclock.microuptime(tvp);
}
void
bintime(struct bintime *bt)
{
sysclock.bintime(bt);
}
void
nanotime(struct timespec *tsp)
{
sysclock.nanotime(tsp);
}
void
microtime(struct timeval *tvp)
{
sysclock.microtime(tvp);
}
void
getbinuptime(struct bintime *bt)
{
sysclock.getbinuptime(bt);
}
void
getnanouptime(struct timespec *tsp)
{
sysclock.getnanouptime(tsp);
}
void
getmicrouptime(struct timeval *tvp)
{
sysclock.getmicrouptime(tvp);
}
void
getbintime(struct bintime *bt)
{
sysclock.getbintime(bt);
}
void
getnanotime(struct timespec *tsp)
{
sysclock.getnanotime(tsp);
}
void
getmicrotime(struct timeval *tvp)
{
sysclock.getmicrouptime(tvp);
}
#endif /* FFCLOCK */ #endif /* FFCLOCK */
/* /*
@ -971,6 +1268,11 @@ tc_windup(void)
scale /= th->th_counter->tc_frequency; scale /= th->th_counter->tc_frequency;
th->th_scale = scale * 2; th->th_scale = scale * 2;
#ifdef FFCLOCK
if (sysclock_active != sysclock.active)
change_sysclock(sysclock_active);
#endif
/* /*
* Now that the struct timehands is again consistent, set the new * Now that the struct timehands is again consistent, set the new
* generation number, making sure to not make it zero. * generation number, making sure to not make it zero.
@ -980,8 +1282,21 @@ tc_windup(void)
th->th_generation = ogen; th->th_generation = ogen;
/* Go live with the new struct timehands. */ /* Go live with the new struct timehands. */
time_second = th->th_microtime.tv_sec; #ifdef FFCLOCK
time_uptime = th->th_offset.sec; switch (sysclock_active) {
case SYSCLOCK_FBCK:
#endif
time_second = th->th_microtime.tv_sec;
time_uptime = th->th_offset.sec;
#ifdef FFCLOCK
break;
case SYSCLOCK_FFWD:
time_second = fftimehands->tick_time_lerp.sec;
time_uptime = fftimehands->tick_time_lerp.sec - ffclock_boottime.sec;
break;
}
#endif
timehands = th; timehands = th;
} }
@ -1261,6 +1576,7 @@ inittimecounter(void *dummy)
#ifdef FFCLOCK #ifdef FFCLOCK
ffclock_init(); ffclock_init();
change_sysclock(sysclock_active);
#endif #endif
/* warm up new timecounter (again) and get rolling. */ /* warm up new timecounter (again) and get rolling. */
(void)timecounter->tc_get_timecount(timecounter); (void)timecounter->tc_get_timecount(timecounter);

View File

@ -55,6 +55,13 @@ struct ffclock_estimate {
#if __BSD_VISIBLE #if __BSD_VISIBLE
#ifdef _KERNEL #ifdef _KERNEL
/*
* Index into the sysclocks array for obtaining the ASCII name of a particular
* sysclock.
*/
#define SYSCLOCK_FBCK 0
#define SYSCLOCK_FFWD 1
/* /*
* Parameters of counter characterisation required by feed-forward algorithms. * Parameters of counter characterisation required by feed-forward algorithms.
*/ */
@ -128,6 +135,35 @@ void ffclock_abstime(ffcounter *ffcount, struct bintime *bt,
void ffclock_difftime(ffcounter ffdelta, struct bintime *bt, void ffclock_difftime(ffcounter ffdelta, struct bintime *bt,
struct bintime *error_bound); struct bintime *error_bound);
/*
* Wrapper routines to return current absolute time using the feed-forward
* clock. These functions are named after those defined in <sys/time.h>, which
* contains a description of the original ones.
*/
void ffclock_bintime(struct bintime *bt);
void ffclock_nanotime(struct timespec *tsp);
void ffclock_microtime(struct timeval *tvp);
void ffclock_getbintime(struct bintime *bt);
void ffclock_getnanotime(struct timespec *tsp);
void ffclock_getmicrotime(struct timeval *tvp);
void ffclock_binuptime(struct bintime *bt);
void ffclock_nanouptime(struct timespec *tsp);
void ffclock_microuptime(struct timeval *tvp);
void ffclock_getbinuptime(struct bintime *bt);
void ffclock_getnanouptime(struct timespec *tsp);
void ffclock_getmicrouptime(struct timeval *tvp);
/*
* Wrapper routines to convert a time interval specified in ffcounter units into
* seconds using the current feed-forward clock estimates.
*/
void ffclock_bindifftime(ffcounter ffdelta, struct bintime *bt);
void ffclock_nanodifftime(ffcounter ffdelta, struct timespec *tsp);
void ffclock_microdifftime(ffcounter ffdelta, struct timeval *tvp);
#endif /* _KERNEL */ #endif /* _KERNEL */
#endif /* __BSD_VISIBLE */ #endif /* __BSD_VISIBLE */
#endif /* _SYS_TIMEFF_H_ */ #endif /* _SYS_TIMEFF_H_ */