123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- #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<<31)/f
- //
- // each time f is set. f is normally set by a user level
- // program writing to /dev/fastclock.
- //
- // 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
- struct {
- ulong cnt;
- Lock;
- vlong multiplier; // t = off + (multiplier*ticks)>>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;
- void
- todinit(void)
- {
- ilock(&tod);
- tod.last = fastticks((uvlong*)&tod.hz);
- iunlock(&tod);
- todsetfreq(tod.hz);
- addclock0link(todfix, 100);
- }
- //
- // calculate multiplier
- //
- void
- todsetfreq(vlong f)
- {
- ilock(&tod);
- tod.hz = f;
- /* calculate multiplier for time conversion */
- tod.multiplier = (TODFREQ<<31)/f;
- iunlock(&tod);
- }
- //
- // Set the time of day struct
- //
- void
- todset(vlong t, vlong delta, int n)
- {
- 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;
- delta = 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.hz == 0)
- ticks = fastticks((uvlong*)&tod.hz);
- else
- ticks = fastticks(nil);
- // since 64 bit loads are not atomic, we have to lock around them
- ilock(&tod);
- tod.cnt++;
- // 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;
- x = (diff * tod.multiplier) >> 31;
- x = 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;
- }
- //
- // called regularly to avoid calculation overflows
- //
- void
- todfix(void)
- {
- vlong ticks, diff;
- uvlong x;
- ticks = fastticks(nil);
- diff = ticks - tod.last;
- if(diff > tod.hz){
- ilock(&tod);
-
- // convert to epoch
- x = diff * tod.multiplier;
- x = x >> 31;
- x = x + tod.off;
-
- // protect against overflows
- tod.last = ticks;
- tod.off = x;
-
- iunlock(&tod);
- }
- }
- long
- seconds(void)
- {
- vlong x;
- int i;
- x = todget(nil);
- x = x/TODFREQ;
- i = x;
- return i;
- }
- // convert milliseconds to fast ticks
- //
- uvlong
- ms2fastticks(ulong ms)
- {
- if(tod.hz == 0)
- fastticks((uvlong*)&tod.hz);
- return (tod.hz*ms)/1000ULL;
- }
|