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
todinit(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
todsetfreq(vlong f)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
todset(vlong t,vlong delta,int n)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
todget(vlong * ticksp)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
tod2fastticks(vlong ns)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
todfix(void)195 todfix(void)
196 {
197 vlong ticks, diff;
198 uvlong x;
199
200 ticks = fastticks(nil);
201
202 diff = ticks - tod.last;
203 if(diff > tod.hz){
204 ilock(&tod);
205
206 /* convert to epoch */
207 mul64fract(&x, diff, tod.multiplier);
208 if(x > 30000000000ULL) iprint("todfix %llud\n", x);
209 x += tod.off;
210
211 /* protect against overflows */
212 tod.last = ticks;
213 tod.off = x;
214
215 iunlock(&tod);
216 }
217 }
218
219 long
seconds(void)220 seconds(void)
221 {
222 return (vlong)todget(nil) / TODFREQ;
223 }
224
225 uvlong
fastticks2us(uvlong ticks)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
us2fastticks(uvlong us)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
ms2fastticks(ulong ms)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
ns2fastticks(uvlong ns)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
fastticks2ns(uvlong ticks)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
mk64fract(uvlong to,uvlong from)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 }
316