summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorlstewart <lstewart@FreeBSD.org>2011-11-20 05:32:12 +0000
committerlstewart <lstewart@FreeBSD.org>2011-11-20 05:32:12 +0000
commit1c7932d8700beaf35065d777ce68888c107cb24a (patch)
tree4349e54bc1cc0ae830281701ae51f5b3b1d38efa
parentad330644b2506d314c5d82704b50881e609948c6 (diff)
downloadFreeBSD-src-1c7932d8700beaf35065d777ce68888c107cb24a.zip
FreeBSD-src-1c7932d8700beaf35065d777ce68888c107cb24a.tar.gz
- 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)
-rw-r--r--sys/kern/kern_ffclock.c214
-rw-r--r--sys/kern/kern_tc.c320
-rw-r--r--sys/sys/timeffc.h36
3 files changed, 568 insertions, 2 deletions
diff --git a/sys/kern/kern_ffclock.c b/sys/kern/kern_ffclock.c
index 14ae5e8..4d31c40 100644
--- a/sys/kern/kern_ffclock.c
+++ b/sys/kern/kern_ffclock.c
@@ -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, struct bintime *bt,
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);
+}
diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c
index 90095f6..66e26d0 100644
--- a/sys/kern/kern_tc.c
+++ b/sys/kern/kern_tc.c
@@ -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-forward clock status. */
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);
diff --git a/sys/sys/timeffc.h b/sys/sys/timeffc.h
index be3c033..c2a04ab 100644
--- a/sys/sys/timeffc.h
+++ b/sys/sys/timeffc.h
@@ -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, struct bintime *bt,
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_ */
OpenPOWER on IntegriCloud