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