]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/tod.c
kernel: use 64-bit virtual entry point for expanded header, document behaviour in...
[plan9front.git] / sys / src / 9 / port / tod.c
1 #include        "u.h"
2 #include        "../port/lib.h"
3 #include        "mem.h"
4 #include        "dat.h"
5 #include        "fns.h"
6 #include        "../port/error.h"
7
8 /*
9  * Compute nanosecond epoch time from the fastest ticking clock
10  * on the system.  Converting the time to nanoseconds requires
11  * the following formula
12  *
13  *      t = (((1000000000<<31)/f)*ticks)>>31
14  *
15  *  where
16  *
17  *      'f'             is the clock frequency
18  *      'ticks'         are clock ticks
19  *
20  *  to avoid too much calculation in todget(), we calculate
21  *
22  *      mult = (1000000000<<32)/f
23  *
24  *  each time f is set.  f is normally set by a user level
25  *  program writing to /dev/fastclock.  mul64fract will then
26  *  take that fractional multiplier and a 64 bit integer and
27  *  return the resulting integer product.
28  *
29  *  We assume that the cpu's of a multiprocessor are synchronized.
30  *  This assumption needs to be questioned with each new architecture.
31  */
32
33 /* frequency of the tod clock */
34 #define TODFREQ         1000000000ULL
35 #define MicroFREQ       1000000ULL
36
37 struct {
38         int     init;           /* true if initialized */
39         ulong   cnt;
40         Lock;
41         uvlong  multiplier;     /* ns = off + (multiplier*ticks)>>31 */
42         uvlong  divider;        /* ticks = (divider*(ns-off))>>31 */
43         uvlong  umultiplier;    /* µs = (µmultiplier*ticks)>>31 */
44         uvlong  udivider;       /* ticks = (µdivider*µs)>>31 */
45         vlong   hz;             /* frequency of fast clock */
46         vlong   last;           /* last reading of fast clock */
47         vlong   off;            /* offset from epoch to last */
48         vlong   lasttime;       /* last return value from todget */
49         vlong   delta;  /* add 'delta' each slow clock tick from sstart to send */
50         ulong   sstart;         /* ... */
51         ulong   send;           /* ... */
52 } tod;
53
54 static void todfix(void);
55
56 void
57 todinit(void)
58 {
59         if(tod.init)
60                 return;
61         ilock(&tod);
62         tod.init = 1;                   /* prevent reentry via fastticks */
63         tod.last = fastticks((uvlong *)&tod.hz);
64         iunlock(&tod);
65         todsetfreq(tod.hz);
66         addclock0link(todfix, 100);
67 }
68
69 /*
70  *  calculate multiplier
71  */
72 void
73 todsetfreq(vlong f)
74 {
75         if (f <= 0)
76                 panic("todsetfreq: freq %lld <= 0", f);
77         ilock(&tod);
78         tod.hz = f;
79
80         /* calculate multiplier for time conversion */
81         tod.multiplier = mk64fract(TODFREQ, f);
82         tod.divider = mk64fract(f, TODFREQ) + 1;
83         tod.umultiplier = mk64fract(MicroFREQ, f);
84         tod.udivider = mk64fract(f, MicroFREQ) + 1;
85         iunlock(&tod);
86 }
87
88 /*
89  *  Set the time of day struct
90  */
91 void
92 todset(vlong t, vlong delta, int n)
93 {
94         if(!tod.init)
95                 todinit();
96
97         ilock(&tod);
98         if(t >= 0){
99                 tod.off = t;
100                 tod.last = fastticks(nil);
101                 tod.lasttime = 0;
102                 tod.delta = 0;
103                 tod.sstart = tod.send;
104         } else {
105                 if(n <= 0)
106                         n = 1;
107                 n *= HZ;
108                 if(delta < 0 && n > -delta)
109                         n = -delta;
110                 if(delta > 0 && n > delta)
111                         n = delta;
112                 if (n == 0) {
113                         iprint("todset: n == 0, delta == %lld\n", delta);
114                         delta = 0;
115                 } else
116                         delta /= n;
117                 tod.sstart = MACHP(0)->ticks;
118                 tod.send = tod.sstart + n;
119                 tod.delta = delta;
120         }
121         iunlock(&tod);
122 }
123
124 /*
125  *  get time of day
126  */
127 vlong
128 todget(vlong *ticksp)
129 {
130         uvlong x;
131         vlong ticks, diff;
132         ulong t;
133
134         if(!tod.init)
135                 todinit();
136
137         /*
138          * we don't want time to pass twixt the measuring of fastticks
139          * and grabbing tod.last.  Also none of the vlongs are atomic so
140          * we have to look at them inside the lock.
141          */
142         ilock(&tod);
143         tod.cnt++;
144         ticks = fastticks(nil);
145
146         /* add in correction */
147         if(tod.sstart != tod.send){
148                 t = MACHP(0)->ticks;
149                 if(t >= tod.send)
150                         t = tod.send;
151                 tod.off = tod.off + tod.delta*(t - tod.sstart);
152                 tod.sstart = t;
153         }
154
155         /* convert to epoch */
156         diff = ticks - tod.last;
157         if(diff < 0)
158                 diff = 0;
159         mul64fract(&x, diff, tod.multiplier);
160         x += tod.off;
161
162         /* time can't go backwards */
163         if(x < tod.lasttime)
164                 x = tod.lasttime;
165         else
166                 tod.lasttime = x;
167
168         iunlock(&tod);
169
170         if(ticksp != nil)
171                 *ticksp = ticks;
172
173         return x;
174 }
175
176 /*
177  *  convert time of day to ticks
178  */
179 uvlong
180 tod2fastticks(vlong ns)
181 {
182         uvlong x;
183
184         ilock(&tod);
185         mul64fract(&x, ns-tod.off, tod.divider);
186         x += tod.last;
187         iunlock(&tod);
188         return x;
189 }
190
191 /*
192  *  called regularly to avoid calculation overflows
193  */
194 static void
195 todfix(void)
196 {
197         vlong ticks, diff;
198         uvlong x;
199
200         ticks = fastticks(nil);
201         diff = ticks - tod.last;
202         if(diff <= tod.hz)
203                 return;
204
205         ilock(&tod);
206         diff = ticks - tod.last;
207         if(diff > tod.hz){
208                 /* convert to epoch */
209                 mul64fract(&x, diff, tod.multiplier);
210                 x += tod.off;
211
212                 /* protect against overflows */
213                 tod.last = ticks;
214                 tod.off = x;
215         }
216         iunlock(&tod);
217 }
218
219 long
220 seconds(void)
221 {
222         return (vlong)todget(nil) / TODFREQ;
223 }
224
225 uvlong
226 fastticks2us(uvlong ticks)
227 {
228         uvlong res;
229
230         if(!tod.init)
231                 todinit();
232         mul64fract(&res, ticks, tod.umultiplier);
233         return res;
234 }
235
236 uvlong
237 us2fastticks(uvlong us)
238 {
239         uvlong res;
240
241         if(!tod.init)
242                 todinit();
243         mul64fract(&res, us, tod.udivider);
244         return res;
245 }
246
247 /*
248  *  convert milliseconds to fast ticks
249  */
250 uvlong
251 ms2fastticks(ulong ms)
252 {
253         if(!tod.init)
254                 todinit();
255         return (tod.hz*ms)/1000ULL;
256 }
257
258 /*
259  *  convert nanoseconds to fast ticks
260  */
261 uvlong
262 ns2fastticks(uvlong ns)
263 {
264         uvlong res;
265
266         if(!tod.init)
267                 todinit();
268         mul64fract(&res, ns, tod.divider);
269         return res;
270 }
271
272 /*
273  *  convert fast ticks to ns
274  */
275 uvlong
276 fastticks2ns(uvlong ticks)
277 {
278         uvlong res;
279
280         if(!tod.init)
281                 todinit();
282         mul64fract(&res, ticks, tod.multiplier);
283         return res;
284 }
285
286 /*
287  * Make a 64 bit fixed point number that has a decimal point
288  * to the left of the low order 32 bits.  This is used with
289  * mul64fract for converting twixt nanoseconds and fastticks.
290  *
291  *      multiplier = (to<<32)/from
292  */
293 uvlong
294 mk64fract(uvlong to, uvlong from)
295 {
296 /*
297         int shift;
298
299         if(to == 0ULL)
300                 return 0ULL;
301
302         shift = 0;
303         while(shift < 32 && to < (1ULL<<(32+24))){
304                 to <<= 8;
305                 shift += 8;
306         }
307         while(shift < 32 && to < (1ULL<<(32+31))){
308                 to <<= 1;
309                 shift += 1;
310         }
311
312         return (to/from)<<(32-shift);
313  */
314         return (to<<32) / from;
315 }