Author: lstewart Date: Sun Nov 20 05:32:12 2011 New Revision: 227747 URL: http://svn.freebsd.org/changeset/base/227747
Log: - 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) Modified: head/sys/kern/kern_ffclock.c head/sys/kern/kern_tc.c head/sys/sys/timeffc.h Modified: head/sys/kern/kern_ffclock.c ============================================================================== --- head/sys/kern/kern_ffclock.c Sun Nov 20 01:48:22 2011 (r227746) +++ head/sys/kern/kern_ffclock.c Sun Nov 20 05:32:12 2011 (r227747) @@ -31,6 +31,8 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/sbuf.h> +#include <sys/sysctl.h> #include <sys/systm.h> #include <sys/timeffc.h> @@ -127,3 +129,215 @@ ffclock_difftime(ffcounter ffdelta, stru 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); +} Modified: head/sys/kern/kern_tc.c ============================================================================== --- head/sys/kern/kern_tc.c Sun Nov 20 01:48:22 2011 (r227746) +++ head/sys/kern/kern_tc.c Sun Nov 20 05:32:12 2011 (r227747) @@ -177,6 +177,144 @@ tc_delta(struct timehands *th) * 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 binuptime(struct bintime *bt) { @@ -313,6 +451,7 @@ getmicrotime(struct timeval *tvp) *tvp = th->th_microtime; } while (gen == 0 || gen != th->th_generation); } +#endif /* FFCLOCK */ #ifdef FFCLOCK /* @@ -322,6 +461,8 @@ 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. */ @@ -329,6 +470,38 @@ uint32_t ffclock_status; /* Feed-forwar int8_t ffclock_updated; /* New estimates are available. */ 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 ffclock_estimate cest; struct bintime tick_time; @@ -621,6 +794,46 @@ ffclock_change_tc(struct timehands *th) 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. */ @@ -731,6 +944,90 @@ ffclock_read_counter(ffcounter *ffcount) *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 */ /* @@ -971,6 +1268,11 @@ tc_windup(void) scale /= th->th_counter->tc_frequency; 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 * generation number, making sure to not make it zero. @@ -980,8 +1282,21 @@ tc_windup(void) th->th_generation = ogen; /* Go live with the new struct timehands. */ - time_second = th->th_microtime.tv_sec; - time_uptime = th->th_offset.sec; +#ifdef FFCLOCK + 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; } @@ -1261,6 +1576,7 @@ inittimecounter(void *dummy) #ifdef FFCLOCK ffclock_init(); + change_sysclock(sysclock_active); #endif /* warm up new timecounter (again) and get rolling. */ (void)timecounter->tc_get_timecount(timecounter); Modified: head/sys/sys/timeffc.h ============================================================================== --- head/sys/sys/timeffc.h Sun Nov 20 01:48:22 2011 (r227746) +++ head/sys/sys/timeffc.h Sun Nov 20 05:32:12 2011 (r227747) @@ -56,6 +56,13 @@ struct ffclock_estimate { #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. */ #define FFCLOCK_SKM_SCALE 1024 @@ -128,6 +135,35 @@ void ffclock_abstime(ffcounter *ffcount, void ffclock_difftime(ffcounter ffdelta, struct bintime *bt, 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 /* __BSD_VISIBLE */ #endif /* _SYS_TIMEFF_H_ */ _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"