summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_clock.c
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2000-03-20 14:09:06 +0000
committerphk <phk@FreeBSD.org>2000-03-20 14:09:06 +0000
commit1a34cea0e84fcb827b7af5b82e87e94c16fd337b (patch)
tree410ee16f844a72e84e097fb4326b0ddaedd2f446 /sys/kern/kern_clock.c
parent1055c7c0e96ac64764f10b1d87889aa4e0ef2ddf (diff)
downloadFreeBSD-src-1a34cea0e84fcb827b7af5b82e87e94c16fd337b.zip
FreeBSD-src-1a34cea0e84fcb827b7af5b82e87e94c16fd337b.tar.gz
Isolate the Timecounter internals in their own two files.
Make the public interface more systematically named. Remove the alternate method, it doesn't do any good, only ruins performance. Add counters to profile the usage of the 8 access functions. Apply the beer-ware to my code. The weird +/- counts are caused by two repocopies behind the scenes: kern/kern_clock.c -> kern/kern_tc.c sys/time.h -> sys/timetc.h (thanks peter!)
Diffstat (limited to 'sys/kern/kern_clock.c')
-rw-r--r--sys/kern/kern_clock.c574
1 files changed, 2 insertions, 572 deletions
diff --git a/sys/kern/kern_clock.c b/sys/kern/kern_clock.c
index 6166e1c..d8066a7 100644
--- a/sys/kern/kern_clock.c
+++ b/sys/kern/kern_clock.c
@@ -1,5 +1,4 @@
/*-
- * Copyright (c) 1997, 1998 Poul-Henning Kamp <phk@FreeBSD.org>
* Copyright (c) 1982, 1986, 1991, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
@@ -51,7 +50,7 @@
#include <sys/malloc.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
-#include <sys/timex.h>
+#include <sys/timetc.h>
#include <sys/timepps.h>
#include <vm/vm.h>
#include <sys/lock.h>
@@ -70,23 +69,9 @@
#include <machine/smp.h>
#endif
-/*
- * Number of timecounters used to implement stable storage
- */
-#ifndef NTIMECOUNTER
-#define NTIMECOUNTER 5
-#endif
-
-static MALLOC_DEFINE(M_TIMECOUNTER, "timecounter",
- "Timecounter stable storage");
-
static void initclocks __P((void *dummy));
SYSINIT(clocks, SI_SUB_CLOCKS, SI_ORDER_FIRST, initclocks, NULL)
-static void tco_forward __P((int force));
-static void tco_setscales __P((struct timecounter *tc));
-static __inline unsigned tco_delta __P((struct timecounter *tc));
-
/* Some of these don't belong here, but it's easiest to concentrate them. */
#if defined(SMP) && defined(BETTER_CLOCK)
long cp_time[CPUSTATES];
@@ -99,42 +84,6 @@ long tk_nin;
long tk_nout;
long tk_rawcc;
-time_t time_second;
-
-struct timeval boottime;
-SYSCTL_STRUCT(_kern, KERN_BOOTTIME, boottime, CTLFLAG_RD,
- &boottime, timeval, "System boottime");
-
-/*
- * Which update policy to use.
- * 0 - every tick, bad hardware may fail with "calcru negative..."
- * 1 - more resistent to the above hardware, but less efficient.
- */
-static int tco_method;
-
-/*
- * Implement a dummy timecounter which we can use until we get a real one
- * in the air. This allows the console and other early stuff to use
- * timeservices.
- */
-
-static unsigned
-dummy_get_timecount(struct timecounter *tc)
-{
- static unsigned now;
- return (++now);
-}
-
-static struct timecounter dummy_timecounter = {
- dummy_get_timecount,
- 0,
- ~0u,
- 1000000,
- "dummy"
-};
-
-struct timecounter *timecounter = &dummy_timecounter;
-
/*
* Clock handling routines.
*
@@ -236,7 +185,7 @@ hardclock(frame)
if (stathz == 0)
statclock(frame);
- tco_forward(0);
+ tc_windup();
ticks++;
/*
@@ -483,522 +432,3 @@ sysctl_kern_clockrate SYSCTL_HANDLER_ARGS
SYSCTL_PROC(_kern, KERN_CLOCKRATE, clockrate, CTLTYPE_STRUCT|CTLFLAG_RD,
0, 0, sysctl_kern_clockrate, "S,clockinfo","");
-
-static __inline unsigned
-tco_delta(struct timecounter *tc)
-{
-
- return ((tc->tc_get_timecount(tc) - tc->tc_offset_count) &
- tc->tc_counter_mask);
-}
-
-/*
- * We have eight functions for looking at the clock, four for
- * microseconds and four for nanoseconds. For each there is fast
- * but less precise version "get{nano|micro}[up]time" which will
- * return a time which is up to 1/HZ previous to the call, whereas
- * the raw version "{nano|micro}[up]time" will return a timestamp
- * which is as precise as possible. The "up" variants return the
- * time relative to system boot, these are well suited for time
- * interval measurements.
- */
-
-void
-getmicrotime(struct timeval *tvp)
-{
- struct timecounter *tc;
-
- if (!tco_method) {
- tc = timecounter;
- *tvp = tc->tc_microtime;
- } else {
- microtime(tvp);
- }
-}
-
-void
-getnanotime(struct timespec *tsp)
-{
- struct timecounter *tc;
-
- if (!tco_method) {
- tc = timecounter;
- *tsp = tc->tc_nanotime;
- } else {
- nanotime(tsp);
- }
-}
-
-void
-microtime(struct timeval *tv)
-{
- struct timecounter *tc;
-
- tc = timecounter;
- tv->tv_sec = tc->tc_offset_sec;
- tv->tv_usec = tc->tc_offset_micro;
- tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32;
- tv->tv_usec += boottime.tv_usec;
- tv->tv_sec += boottime.tv_sec;
- while (tv->tv_usec >= 1000000) {
- tv->tv_usec -= 1000000;
- tv->tv_sec++;
- }
-}
-
-void
-nanotime(struct timespec *ts)
-{
- unsigned count;
- u_int64_t delta;
- struct timecounter *tc;
-
- tc = timecounter;
- ts->tv_sec = tc->tc_offset_sec;
- count = tco_delta(tc);
- delta = tc->tc_offset_nano;
- delta += ((u_int64_t)count * tc->tc_scale_nano_f);
- delta >>= 32;
- delta += ((u_int64_t)count * tc->tc_scale_nano_i);
- delta += boottime.tv_usec * 1000;
- ts->tv_sec += boottime.tv_sec;
- while (delta >= 1000000000) {
- delta -= 1000000000;
- ts->tv_sec++;
- }
- ts->tv_nsec = delta;
-}
-
-void
-getmicrouptime(struct timeval *tvp)
-{
- struct timecounter *tc;
-
- if (!tco_method) {
- tc = timecounter;
- tvp->tv_sec = tc->tc_offset_sec;
- tvp->tv_usec = tc->tc_offset_micro;
- } else {
- microuptime(tvp);
- }
-}
-
-void
-getnanouptime(struct timespec *tsp)
-{
- struct timecounter *tc;
-
- if (!tco_method) {
- tc = timecounter;
- tsp->tv_sec = tc->tc_offset_sec;
- tsp->tv_nsec = tc->tc_offset_nano >> 32;
- } else {
- nanouptime(tsp);
- }
-}
-
-void
-microuptime(struct timeval *tv)
-{
- struct timecounter *tc;
-
- tc = timecounter;
- tv->tv_sec = tc->tc_offset_sec;
- tv->tv_usec = tc->tc_offset_micro;
- tv->tv_usec += ((u_int64_t)tco_delta(tc) * tc->tc_scale_micro) >> 32;
- if (tv->tv_usec >= 1000000) {
- tv->tv_usec -= 1000000;
- tv->tv_sec++;
- }
-}
-
-void
-nanouptime(struct timespec *ts)
-{
- unsigned count;
- u_int64_t delta;
- struct timecounter *tc;
-
- tc = timecounter;
- ts->tv_sec = tc->tc_offset_sec;
- count = tco_delta(tc);
- delta = tc->tc_offset_nano;
- delta += ((u_int64_t)count * tc->tc_scale_nano_f);
- delta >>= 32;
- delta += ((u_int64_t)count * tc->tc_scale_nano_i);
- if (delta >= 1000000000) {
- delta -= 1000000000;
- ts->tv_sec++;
- }
- ts->tv_nsec = delta;
-}
-
-static void
-tco_setscales(struct timecounter *tc)
-{
- u_int64_t scale;
-
- scale = 1000000000LL << 32;
- scale += tc->tc_adjustment;
- scale /= tc->tc_tweak->tc_frequency;
- tc->tc_scale_micro = scale / 1000;
- tc->tc_scale_nano_f = scale & 0xffffffff;
- tc->tc_scale_nano_i = scale >> 32;
-}
-
-void
-update_timecounter(struct timecounter *tc)
-{
- tco_setscales(tc);
-}
-
-void
-init_timecounter(struct timecounter *tc)
-{
- struct timespec ts1;
- struct timecounter *t1, *t2, *t3;
- int i;
-
- tc->tc_adjustment = 0;
- tc->tc_tweak = tc;
- tco_setscales(tc);
- tc->tc_offset_count = tc->tc_get_timecount(tc);
- if (timecounter == &dummy_timecounter)
- tc->tc_avail = tc;
- else {
- tc->tc_avail = timecounter->tc_tweak->tc_avail;
- timecounter->tc_tweak->tc_avail = tc;
- }
- MALLOC(t1, struct timecounter *, sizeof *t1, M_TIMECOUNTER, M_WAITOK);
- tc->tc_other = t1;
- *t1 = *tc;
- t2 = t1;
- for (i = 1; i < NTIMECOUNTER; i++) {
- MALLOC(t3, struct timecounter *, sizeof *t3,
- M_TIMECOUNTER, M_WAITOK);
- *t3 = *tc;
- t3->tc_other = t2;
- t2 = t3;
- }
- t1->tc_other = t3;
- tc = t1;
-
- printf("Timecounter \"%s\" frequency %lu Hz\n",
- tc->tc_name, (u_long)tc->tc_frequency);
-
- /* XXX: For now always start using the counter. */
- tc->tc_offset_count = tc->tc_get_timecount(tc);
- nanouptime(&ts1);
- tc->tc_offset_nano = (u_int64_t)ts1.tv_nsec << 32;
- tc->tc_offset_micro = ts1.tv_nsec / 1000;
- tc->tc_offset_sec = ts1.tv_sec;
- timecounter = tc;
-}
-
-void
-set_timecounter(struct timespec *ts)
-{
- struct timespec ts2;
-
- nanouptime(&ts2);
- boottime.tv_sec = ts->tv_sec - ts2.tv_sec;
- boottime.tv_usec = (ts->tv_nsec - ts2.tv_nsec) / 1000;
- if (boottime.tv_usec < 0) {
- boottime.tv_usec += 1000000;
- boottime.tv_sec--;
- }
- /* fiddle all the little crinkly bits around the fiords... */
- tco_forward(1);
-}
-
-static void
-switch_timecounter(struct timecounter *newtc)
-{
- int s;
- struct timecounter *tc;
- struct timespec ts;
-
- s = splclock();
- tc = timecounter;
- if (newtc->tc_tweak == tc->tc_tweak) {
- splx(s);
- return;
- }
- newtc = newtc->tc_tweak->tc_other;
- nanouptime(&ts);
- newtc->tc_offset_sec = ts.tv_sec;
- newtc->tc_offset_nano = (u_int64_t)ts.tv_nsec << 32;
- newtc->tc_offset_micro = ts.tv_nsec / 1000;
- newtc->tc_offset_count = newtc->tc_get_timecount(newtc);
- tco_setscales(newtc);
- timecounter = newtc;
- splx(s);
-}
-
-static struct timecounter *
-sync_other_counter(void)
-{
- struct timecounter *tc, *tcn, *tco;
- unsigned delta;
-
- tco = timecounter;
- tc = tco->tc_other;
- tcn = tc->tc_other;
- *tc = *tco;
- tc->tc_other = tcn;
- delta = tco_delta(tc);
- tc->tc_offset_count += delta;
- tc->tc_offset_count &= tc->tc_counter_mask;
- tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_f;
- tc->tc_offset_nano += (u_int64_t)delta * tc->tc_scale_nano_i << 32;
- return (tc);
-}
-
-static void
-tco_forward(int force)
-{
- struct timecounter *tc, *tco;
- struct timeval tvt;
-
- tco = timecounter;
- tc = sync_other_counter();
- /*
- * We may be inducing a tiny error here, the tc_poll_pps() may
- * process a latched count which happens after the tco_delta()
- * in sync_other_counter(), which would extend the previous
- * counters parameters into the domain of this new one.
- * Since the timewindow is very small for this, the error is
- * going to be only a few weenieseconds (as Dave Mills would
- * say), so lets just not talk more about it, OK ?
- */
- if (tco->tc_poll_pps)
- tco->tc_poll_pps(tco);
- if (timedelta != 0) {
- tvt = boottime;
- tvt.tv_usec += tickdelta;
- if (tvt.tv_usec >= 1000000) {
- tvt.tv_sec++;
- tvt.tv_usec -= 1000000;
- } else if (tvt.tv_usec < 0) {
- tvt.tv_sec--;
- tvt.tv_usec += 1000000;
- }
- boottime = tvt;
- timedelta -= tickdelta;
- }
-
- while (tc->tc_offset_nano >= 1000000000ULL << 32) {
- tc->tc_offset_nano -= 1000000000ULL << 32;
- tc->tc_offset_sec++;
- ntp_update_second(tc); /* XXX only needed if xntpd runs */
- tco_setscales(tc);
- force++;
- }
-
- if (tco_method && !force)
- return;
-
- tc->tc_offset_micro = (tc->tc_offset_nano / 1000) >> 32;
-
- /* Figure out the wall-clock time */
- tc->tc_nanotime.tv_sec = tc->tc_offset_sec + boottime.tv_sec;
- tc->tc_nanotime.tv_nsec =
- (tc->tc_offset_nano >> 32) + boottime.tv_usec * 1000;
- tc->tc_microtime.tv_usec = tc->tc_offset_micro + boottime.tv_usec;
- if (tc->tc_nanotime.tv_nsec >= 1000000000) {
- tc->tc_nanotime.tv_nsec -= 1000000000;
- tc->tc_microtime.tv_usec -= 1000000;
- tc->tc_nanotime.tv_sec++;
- }
- time_second = tc->tc_microtime.tv_sec = tc->tc_nanotime.tv_sec;
-
- timecounter = tc;
-}
-
-SYSCTL_NODE(_kern, OID_AUTO, timecounter, CTLFLAG_RW, 0, "");
-
-SYSCTL_INT(_kern_timecounter, OID_AUTO, method, CTLFLAG_RW, &tco_method, 0,
- "This variable determines the method used for updating timecounters. "
- "If the default algorithm (0) fails with \"calcru negative...\" messages "
- "try the alternate algorithm (1) which handles bad hardware better."
-
-);
-
-static int
-sysctl_kern_timecounter_hardware SYSCTL_HANDLER_ARGS
-{
- char newname[32];
- struct timecounter *newtc, *tc;
- int error;
-
- tc = timecounter->tc_tweak;
- strncpy(newname, tc->tc_name, sizeof(newname));
- error = sysctl_handle_string(oidp, &newname[0], sizeof(newname), req);
- if (error == 0 && req->newptr != NULL &&
- strcmp(newname, tc->tc_name) != 0) {
- for (newtc = tc->tc_avail; newtc != tc;
- newtc = newtc->tc_avail) {
- if (strcmp(newname, newtc->tc_name) == 0) {
- /* Warm up new timecounter. */
- (void)newtc->tc_get_timecount(newtc);
-
- switch_timecounter(newtc);
- return (0);
- }
- }
- return (EINVAL);
- }
- return (error);
-}
-
-SYSCTL_PROC(_kern_timecounter, OID_AUTO, hardware, CTLTYPE_STRING | CTLFLAG_RW,
- 0, 0, sysctl_kern_timecounter_hardware, "A", "");
-
-
-int
-pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps)
-{
- pps_params_t *app;
- struct pps_fetch_args *fapi;
-#ifdef PPS_SYNC
- struct pps_kcbind_args *kapi;
-#endif
-
- switch (cmd) {
- case PPS_IOC_CREATE:
- return (0);
- case PPS_IOC_DESTROY:
- return (0);
- case PPS_IOC_SETPARAMS:
- app = (pps_params_t *)data;
- if (app->mode & ~pps->ppscap)
- return (EINVAL);
- pps->ppsparam = *app;
- return (0);
- case PPS_IOC_GETPARAMS:
- app = (pps_params_t *)data;
- *app = pps->ppsparam;
- app->api_version = PPS_API_VERS_1;
- return (0);
- case PPS_IOC_GETCAP:
- *(int*)data = pps->ppscap;
- return (0);
- case PPS_IOC_FETCH:
- fapi = (struct pps_fetch_args *)data;
- if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC)
- return (EINVAL);
- if (fapi->timeout.tv_sec || fapi->timeout.tv_nsec)
- return (EOPNOTSUPP);
- pps->ppsinfo.current_mode = pps->ppsparam.mode;
- fapi->pps_info_buf = pps->ppsinfo;
- return (0);
- case PPS_IOC_KCBIND:
-#ifdef PPS_SYNC
- kapi = (struct pps_kcbind_args *)data;
- /* XXX Only root should be able to do this */
- if (kapi->tsformat && kapi->tsformat != PPS_TSFMT_TSPEC)
- return (EINVAL);
- if (kapi->kernel_consumer != PPS_KC_HARDPPS)
- return (EINVAL);
- if (kapi->edge & ~pps->ppscap)
- return (EINVAL);
- pps->kcmode = kapi->edge;
- return (0);
-#else
- return (EOPNOTSUPP);
-#endif
- default:
- return (ENOTTY);
- }
-}
-
-void
-pps_init(struct pps_state *pps)
-{
- pps->ppscap |= PPS_TSFMT_TSPEC;
- if (pps->ppscap & PPS_CAPTUREASSERT)
- pps->ppscap |= PPS_OFFSETASSERT;
- if (pps->ppscap & PPS_CAPTURECLEAR)
- pps->ppscap |= PPS_OFFSETCLEAR;
-}
-
-void
-pps_event(struct pps_state *pps, struct timecounter *tc, unsigned count, int event)
-{
- struct timespec ts, *tsp, *osp;
- u_int64_t delta;
- unsigned tcount, *pcount;
- int foff, fhard;
- pps_seq_t *pseq;
-
- /* Things would be easier with arrays... */
- if (event == PPS_CAPTUREASSERT) {
- tsp = &pps->ppsinfo.assert_timestamp;
- osp = &pps->ppsparam.assert_offset;
- foff = pps->ppsparam.mode & PPS_OFFSETASSERT;
- fhard = pps->kcmode & PPS_CAPTUREASSERT;
- pcount = &pps->ppscount[0];
- pseq = &pps->ppsinfo.assert_sequence;
- } else {
- tsp = &pps->ppsinfo.clear_timestamp;
- osp = &pps->ppsparam.clear_offset;
- foff = pps->ppsparam.mode & PPS_OFFSETCLEAR;
- fhard = pps->kcmode & PPS_CAPTURECLEAR;
- pcount = &pps->ppscount[1];
- pseq = &pps->ppsinfo.clear_sequence;
- }
-
- /* The timecounter changed: bail */
- if (!pps->ppstc ||
- pps->ppstc->tc_name != tc->tc_name ||
- tc->tc_name != timecounter->tc_name) {
- pps->ppstc = tc;
- *pcount = count;
- return;
- }
-
- /* Nothing really happened */
- if (*pcount == count)
- return;
-
- *pcount = count;
-
- /* Convert the count to timespec */
- ts.tv_sec = tc->tc_offset_sec;
- tcount = count - tc->tc_offset_count;
- tcount &= tc->tc_counter_mask;
- delta = tc->tc_offset_nano;
- delta += ((u_int64_t)tcount * tc->tc_scale_nano_f);
- delta >>= 32;
- delta += ((u_int64_t)tcount * tc->tc_scale_nano_i);
- delta += boottime.tv_usec * 1000;
- ts.tv_sec += boottime.tv_sec;
- while (delta >= 1000000000) {
- delta -= 1000000000;
- ts.tv_sec++;
- }
- ts.tv_nsec = delta;
-
- (*pseq)++;
- *tsp = ts;
-
- if (foff) {
- timespecadd(tsp, osp);
- if (tsp->tv_nsec < 0) {
- tsp->tv_nsec += 1000000000;
- tsp->tv_sec -= 1;
- }
- }
-#ifdef PPS_SYNC
- if (fhard) {
- /* magic, at its best... */
- tcount = count - pps->ppscount[2];
- pps->ppscount[2] = count;
- tcount &= tc->tc_counter_mask;
- delta = ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_f);
- delta >>= 32;
- delta += ((u_int64_t)tcount * tc->tc_tweak->tc_scale_nano_i);
- hardpps(tsp, delta);
- }
-#endif
-}
OpenPOWER on IntegriCloud