summaryrefslogtreecommitdiff
path: root/sys/src/9/port/tod.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/tod.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/tod.c')
-rwxr-xr-xsys/src/9/port/tod.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/sys/src/9/port/tod.c b/sys/src/9/port/tod.c
new file mode 100755
index 000000000..424e0f290
--- /dev/null
+++ b/sys/src/9/port/tod.c
@@ -0,0 +1,315 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+/*
+ * Compute nanosecond epoch time from the fastest ticking clock
+ * on the system. Converting the time to nanoseconds requires
+ * the following formula
+ *
+ * t = (((1000000000<<31)/f)*ticks)>>31
+ *
+ * where
+ *
+ * 'f' is the clock frequency
+ * 'ticks' are clock ticks
+ *
+ * to avoid too much calculation in todget(), we calculate
+ *
+ * mult = (1000000000<<32)/f
+ *
+ * each time f is set. f is normally set by a user level
+ * program writing to /dev/fastclock. mul64fract will then
+ * take that fractional multiplier and a 64 bit integer and
+ * return the resulting integer product.
+ *
+ * We assume that the cpu's of a multiprocessor are synchronized.
+ * This assumption needs to be questioned with each new architecture.
+ */
+
+/* frequency of the tod clock */
+#define TODFREQ 1000000000ULL
+#define MicroFREQ 1000000ULL
+
+struct {
+ int init; /* true if initialized */
+ ulong cnt;
+ Lock;
+ uvlong multiplier; /* ns = off + (multiplier*ticks)>>31 */
+ uvlong divider; /* ticks = (divider*(ns-off))>>31 */
+ uvlong umultiplier; /* µs = (µmultiplier*ticks)>>31 */
+ uvlong udivider; /* ticks = (µdivider*µs)>>31 */
+ vlong hz; /* frequency of fast clock */
+ vlong last; /* last reading of fast clock */
+ vlong off; /* offset from epoch to last */
+ vlong lasttime; /* last return value from todget */
+ vlong delta; /* add 'delta' each slow clock tick from sstart to send */
+ ulong sstart; /* ... */
+ ulong send; /* ... */
+} tod;
+
+static void todfix(void);
+
+void
+todinit(void)
+{
+ if(tod.init)
+ return;
+ ilock(&tod);
+ tod.init = 1; /* prevent reentry via fastticks */
+ tod.last = fastticks((uvlong *)&tod.hz);
+ iunlock(&tod);
+ todsetfreq(tod.hz);
+ addclock0link(todfix, 100);
+}
+
+/*
+ * calculate multiplier
+ */
+void
+todsetfreq(vlong f)
+{
+ if (f <= 0)
+ panic("todsetfreq: freq %lld <= 0", f);
+ ilock(&tod);
+ tod.hz = f;
+
+ /* calculate multiplier for time conversion */
+ tod.multiplier = mk64fract(TODFREQ, f);
+ tod.divider = mk64fract(f, TODFREQ) + 1;
+ tod.umultiplier = mk64fract(MicroFREQ, f);
+ tod.udivider = mk64fract(f, MicroFREQ) + 1;
+ iunlock(&tod);
+}
+
+/*
+ * Set the time of day struct
+ */
+void
+todset(vlong t, vlong delta, int n)
+{
+ if(!tod.init)
+ todinit();
+
+ ilock(&tod);
+ if(t >= 0){
+ tod.off = t;
+ tod.last = fastticks(nil);
+ tod.lasttime = 0;
+ tod.delta = 0;
+ tod.sstart = tod.send;
+ } else {
+ if(n <= 0)
+ n = 1;
+ n *= HZ;
+ if(delta < 0 && n > -delta)
+ n = -delta;
+ if(delta > 0 && n > delta)
+ n = delta;
+ if (n == 0) {
+ iprint("todset: n == 0, delta == %lld\n", delta);
+ delta = 0;
+ } else
+ delta /= n;
+ tod.sstart = MACHP(0)->ticks;
+ tod.send = tod.sstart + n;
+ tod.delta = delta;
+ }
+ iunlock(&tod);
+}
+
+/*
+ * get time of day
+ */
+vlong
+todget(vlong *ticksp)
+{
+ uvlong x;
+ vlong ticks, diff;
+ ulong t;
+
+ if(!tod.init)
+ todinit();
+
+ /*
+ * we don't want time to pass twixt the measuring of fastticks
+ * and grabbing tod.last. Also none of the vlongs are atomic so
+ * we have to look at them inside the lock.
+ */
+ ilock(&tod);
+ tod.cnt++;
+ ticks = fastticks(nil);
+
+ /* add in correction */
+ if(tod.sstart != tod.send){
+ t = MACHP(0)->ticks;
+ if(t >= tod.send)
+ t = tod.send;
+ tod.off = tod.off + tod.delta*(t - tod.sstart);
+ tod.sstart = t;
+ }
+
+ /* convert to epoch */
+ diff = ticks - tod.last;
+ if(diff < 0)
+ diff = 0;
+ mul64fract(&x, diff, tod.multiplier);
+ x += tod.off;
+
+ /* time can't go backwards */
+ if(x < tod.lasttime)
+ x = tod.lasttime;
+ else
+ tod.lasttime = x;
+
+ iunlock(&tod);
+
+ if(ticksp != nil)
+ *ticksp = ticks;
+
+ return x;
+}
+
+/*
+ * convert time of day to ticks
+ */
+uvlong
+tod2fastticks(vlong ns)
+{
+ uvlong x;
+
+ ilock(&tod);
+ mul64fract(&x, ns-tod.off, tod.divider);
+ x += tod.last;
+ iunlock(&tod);
+ return x;
+}
+
+/*
+ * called regularly to avoid calculation overflows
+ */
+static void
+todfix(void)
+{
+ vlong ticks, diff;
+ uvlong x;
+
+ ticks = fastticks(nil);
+
+ diff = ticks - tod.last;
+ if(diff > tod.hz){
+ ilock(&tod);
+
+ /* convert to epoch */
+ mul64fract(&x, diff, tod.multiplier);
+if(x > 30000000000ULL) iprint("todfix %llud\n", x);
+ x += tod.off;
+
+ /* protect against overflows */
+ tod.last = ticks;
+ tod.off = x;
+
+ iunlock(&tod);
+ }
+}
+
+long
+seconds(void)
+{
+ return (vlong)todget(nil) / TODFREQ;
+}
+
+uvlong
+fastticks2us(uvlong ticks)
+{
+ uvlong res;
+
+ if(!tod.init)
+ todinit();
+ mul64fract(&res, ticks, tod.umultiplier);
+ return res;
+}
+
+uvlong
+us2fastticks(uvlong us)
+{
+ uvlong res;
+
+ if(!tod.init)
+ todinit();
+ mul64fract(&res, us, tod.udivider);
+ return res;
+}
+
+/*
+ * convert milliseconds to fast ticks
+ */
+uvlong
+ms2fastticks(ulong ms)
+{
+ if(!tod.init)
+ todinit();
+ return (tod.hz*ms)/1000ULL;
+}
+
+/*
+ * convert nanoseconds to fast ticks
+ */
+uvlong
+ns2fastticks(uvlong ns)
+{
+ uvlong res;
+
+ if(!tod.init)
+ todinit();
+ mul64fract(&res, ns, tod.divider);
+ return res;
+}
+
+/*
+ * convert fast ticks to ns
+ */
+uvlong
+fastticks2ns(uvlong ticks)
+{
+ uvlong res;
+
+ if(!tod.init)
+ todinit();
+ mul64fract(&res, ticks, tod.multiplier);
+ return res;
+}
+
+/*
+ * Make a 64 bit fixed point number that has a decimal point
+ * to the left of the low order 32 bits. This is used with
+ * mul64fract for converting twixt nanoseconds and fastticks.
+ *
+ * multiplier = (to<<32)/from
+ */
+uvlong
+mk64fract(uvlong to, uvlong from)
+{
+/*
+ int shift;
+
+ if(to == 0ULL)
+ return 0ULL;
+
+ shift = 0;
+ while(shift < 32 && to < (1ULL<<(32+24))){
+ to <<= 8;
+ shift += 8;
+ }
+ while(shift < 32 && to < (1ULL<<(32+31))){
+ to <<= 1;
+ shift += 1;
+ }
+
+ return (to/from)<<(32-shift);
+ */
+ return (to<<32) / from;
+}