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 /* 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 35 struct { 36 int init; // true if initialized 37 ulong cnt; 38 Lock; 39 uvlong multiplier; // t = off + (multiplier*ticks)>>31 40 uvlong divider; // ticks = (divider*(ticks-off))>>31 41 vlong hz; // frequency of fast clock 42 vlong last; // last reading of fast clock 43 vlong off; // offset from epoch to last 44 vlong lasttime; // last return value from todget 45 vlong delta; // add 'delta' each slow clock tick from sstart to send 46 ulong sstart; // ... 47 ulong send; // ... 48 } tod; 49 50 void 51 todinit(void) 52 { 53 if(tod.init) 54 return; 55 ilock(&tod); 56 tod.last = fastticks((uvlong*)&tod.hz); 57 iunlock(&tod); 58 todsetfreq(tod.hz); 59 tod.init = 1; 60 addclock0link(todfix, 100); 61 } 62 63 /* 64 * calculate multiplier 65 */ 66 void 67 todsetfreq(vlong f) 68 { 69 ilock(&tod); 70 tod.hz = f; 71 72 /* calculate multiplier for time conversion */ 73 tod.multiplier = mk64fract(TODFREQ, f); 74 tod.divider = mk64fract(f, TODFREQ); 75 76 iunlock(&tod); 77 } 78 79 /* 80 * Set the time of day struct 81 */ 82 void 83 todset(vlong t, vlong delta, int n) 84 { 85 if(!tod.init) 86 todinit(); 87 88 ilock(&tod); 89 if(t >= 0){ 90 tod.off = t; 91 tod.last = fastticks(nil); 92 tod.lasttime = 0; 93 tod.delta = 0; 94 tod.sstart = tod.send; 95 } else { 96 if(n <= 0) 97 n = 1; 98 n *= HZ; 99 if(delta < 0 && n > -delta) 100 n = -delta; 101 if(delta > 0 && n > delta) 102 n = delta; 103 delta = delta/n; 104 tod.sstart = MACHP(0)->ticks; 105 tod.send = tod.sstart + n; 106 tod.delta = delta; 107 } 108 iunlock(&tod); 109 } 110 111 /* 112 * get time of day 113 */ 114 vlong 115 todget(vlong *ticksp) 116 { 117 uvlong x; 118 vlong ticks, diff; 119 ulong t; 120 121 if(!tod.init) 122 todinit(); 123 124 // we don't want time to pass twixt the measuring of fastticks 125 // and grabbing tod.last. Also none of the vlongs are atomic so 126 // we have to look at them inside the lock. 127 ilock(&tod); 128 tod.cnt++; 129 ticks = fastticks(nil); 130 131 // add in correction 132 if(tod.sstart != tod.send){ 133 t = MACHP(0)->ticks; 134 if(t >= tod.send) 135 t = tod.send; 136 tod.off = tod.off + tod.delta*(t - tod.sstart); 137 tod.sstart = t; 138 } 139 140 // convert to epoch 141 diff = ticks - tod.last; 142 if(diff < 0) 143 diff = 0; 144 mul64fract(&x, diff, tod.multiplier); 145 x += tod.off; 146 147 // time can't go backwards 148 if(x < tod.lasttime) 149 x = tod.lasttime; 150 else 151 tod.lasttime = x; 152 153 iunlock(&tod); 154 155 if(ticksp != nil) 156 *ticksp = ticks; 157 158 return x; 159 } 160 161 /* 162 * convert time of day to ticks 163 */ 164 uvlong 165 tod2fastticks(vlong ns) 166 { 167 uvlong x; 168 169 ilock(&tod); 170 mul64fract(&x, ns-tod.off, tod.divider); 171 x += tod.last; 172 iunlock(&tod); 173 return x; 174 } 175 176 /* 177 * called regularly to avoid calculation overflows 178 */ 179 void 180 todfix(void) 181 { 182 vlong ticks, diff; 183 uvlong x; 184 185 ticks = fastticks(nil); 186 187 diff = ticks - tod.last; 188 if(diff > tod.hz){ 189 ilock(&tod); 190 191 // convert to epoch 192 mul64fract(&x, diff, tod.multiplier); 193 if(x > 30000000000ULL) print("todfix %llud\n", x); 194 x += tod.off; 195 196 // protect against overflows 197 tod.last = ticks; 198 tod.off = x; 199 200 iunlock(&tod); 201 } 202 } 203 204 long 205 tseconds(void) 206 { 207 vlong x; 208 int i; 209 210 x = todget(nil); 211 x = x/TODFREQ; 212 i = x; 213 return i; 214 } 215 216 // convert milliseconds to fast ticks 217 // 218 uvlong 219 ms2fastticks(ulong ms) 220 { 221 if(!tod.init) 222 todinit(); 223 return (tod.hz*ms)/1000ULL; 224 } 225 226 /* 227 * convert nanoseconds to fast ticks 228 */ 229 uvlong 230 ns2fastticks(uvlong ns) 231 { 232 uvlong res; 233 234 if(!tod.init) 235 todinit(); 236 mul64fract(&res, ns, tod.divider); 237 return res; 238 } 239 240 /* 241 * convert fast ticks to ns 242 */ 243 uvlong 244 fastticks2ns(uvlong ticks) 245 { 246 uvlong res; 247 248 if(!tod.init) 249 todinit(); 250 mul64fract(&res, ticks, tod.multiplier); 251 return res; 252 } 253 254 /* 255 * Make a 64 bit fixed point number that has a decimal point 256 * to the left of the low order 32 bits. This is used with 257 * mul64fract for converting twixt nanoseconds and fastticks. 258 * 259 * multiplier = (to<<32)/from 260 */ 261 uvlong 262 mk64fract(uvlong to, uvlong from) 263 { 264 /* 265 int shift; 266 267 if(to == 0ULL) 268 return 0ULL; 269 270 shift = 0; 271 while(shift < 32 && to < (1ULL<<(32+24))){ 272 to <<= 8; 273 shift += 8; 274 } 275 while(shift < 32 && to < (1ULL<<(32+31))){ 276 to <<= 1; 277 shift += 1; 278 } 279 280 return (to/from)<<(32-shift); 281 */ 282 return (to<<32)/from; 283 } 284