17dd7cddfSDavid du Colombier #include <u.h> 27dd7cddfSDavid du Colombier #include <libc.h> 35d459b5aSDavid du Colombier #include <auth.h> 47dd7cddfSDavid du Colombier #include <ip.h> 580ee5cbfSDavid du Colombier #include <mp.h> 67dd7cddfSDavid du Colombier 780ee5cbfSDavid du Colombier /* nanosecond times */ 87dd7cddfSDavid du Colombier #define SEC 1000000000LL 980ee5cbfSDavid du Colombier #define MIN (60LL*SEC) 1080ee5cbfSDavid du Colombier #define HOUR (60LL*MIN) 1180ee5cbfSDavid du Colombier #define DAY (24LL*HOUR) 127dd7cddfSDavid du Colombier 137dd7cddfSDavid du Colombier enum { 147dd7cddfSDavid du Colombier Fs, 157dd7cddfSDavid du Colombier Rtc, 167dd7cddfSDavid du Colombier Ntp, 175fab9909SDavid du Colombier Utc, 1880ee5cbfSDavid du Colombier 1980ee5cbfSDavid du Colombier HZAvgSecs= 3*60, /* target averaging period for the frequency in seconds */ 2080ee5cbfSDavid du Colombier MinSampleSecs= 60, /* minimum sampling time in seconds */ 217dd7cddfSDavid du Colombier }; 227dd7cddfSDavid du Colombier 237dd7cddfSDavid du Colombier 247dd7cddfSDavid du Colombier char *dir = "/tmp"; // directory sample files live in 257dd7cddfSDavid du Colombier char *logfile = "timesync"; 267dd7cddfSDavid du Colombier char *timeserver; 275fab9909SDavid du Colombier char *Rootid; 285fab9909SDavid du Colombier int utcfil; 297dd7cddfSDavid du Colombier int debug; 307dd7cddfSDavid du Colombier int impotent; 317dd7cddfSDavid du Colombier int logging; 327dd7cddfSDavid du Colombier int type; 337dd7cddfSDavid du Colombier int gmtdelta; // rtc+gmtdelta = gmt 3495fdf19cSDavid du Colombier uvlong avgerr; 357dd7cddfSDavid du Colombier 367dd7cddfSDavid du Colombier // ntp server info 377dd7cddfSDavid du Colombier int stratum = 14; 3880ee5cbfSDavid du Colombier vlong mydisp, rootdisp; 3980ee5cbfSDavid du Colombier vlong mydelay, rootdelay; 407dd7cddfSDavid du Colombier vlong avgdelay; 411118d624SDavid du Colombier vlong lastutc; 427dd7cddfSDavid du Colombier uchar rootid[4]; 437dd7cddfSDavid du Colombier char *sysid; 4467031067SDavid du Colombier int myprec; 457dd7cddfSDavid du Colombier 467dd7cddfSDavid du Colombier // list of time samples 477dd7cddfSDavid du Colombier typedef struct Sample Sample; 487dd7cddfSDavid du Colombier struct Sample 497dd7cddfSDavid du Colombier { 507dd7cddfSDavid du Colombier Sample *next; 517dd7cddfSDavid du Colombier uvlong ticks; 527dd7cddfSDavid du Colombier vlong ltime; 537dd7cddfSDavid du Colombier vlong stime; 547dd7cddfSDavid du Colombier }; 557dd7cddfSDavid du Colombier 567dd7cddfSDavid du Colombier // ntp packet 577dd7cddfSDavid du Colombier typedef struct NTPpkt NTPpkt; 587dd7cddfSDavid du Colombier struct NTPpkt 597dd7cddfSDavid du Colombier { 607dd7cddfSDavid du Colombier uchar mode; 617dd7cddfSDavid du Colombier uchar stratum; 627dd7cddfSDavid du Colombier uchar poll; 637dd7cddfSDavid du Colombier uchar precision; 647dd7cddfSDavid du Colombier uchar rootdelay[4]; 657dd7cddfSDavid du Colombier uchar rootdisp[4]; 667dd7cddfSDavid du Colombier uchar rootid[4]; 677dd7cddfSDavid du Colombier uchar refts[8]; 687dd7cddfSDavid du Colombier uchar origts[8]; // departed client 697dd7cddfSDavid du Colombier uchar recvts[8]; // arrived at server 707dd7cddfSDavid du Colombier uchar xmitts[8]; // departed server 717dd7cddfSDavid du Colombier uchar keyid[4]; 727dd7cddfSDavid du Colombier uchar digest[16]; 737dd7cddfSDavid du Colombier }; 747dd7cddfSDavid du Colombier 7580ee5cbfSDavid du Colombier // ntp server 7680ee5cbfSDavid du Colombier typedef struct NTPserver NTPserver; 7780ee5cbfSDavid du Colombier struct NTPserver 7880ee5cbfSDavid du Colombier { 7980ee5cbfSDavid du Colombier NTPserver *next; 8080ee5cbfSDavid du Colombier char *name; 8180ee5cbfSDavid du Colombier uchar stratum; 8280ee5cbfSDavid du Colombier uchar precision; 8380ee5cbfSDavid du Colombier vlong rootdelay; 8480ee5cbfSDavid du Colombier vlong rootdisp; 8580ee5cbfSDavid du Colombier vlong rtt; 8680ee5cbfSDavid du Colombier vlong dt; 8780ee5cbfSDavid du Colombier }; 8880ee5cbfSDavid du Colombier 8980ee5cbfSDavid du Colombier NTPserver *ntpservers; 9080ee5cbfSDavid du Colombier 917dd7cddfSDavid du Colombier enum 927dd7cddfSDavid du Colombier { 937dd7cddfSDavid du Colombier NTPSIZE= 48, // basic ntp packet 947dd7cddfSDavid du Colombier NTPDIGESTSIZE= 20, // key and digest 957dd7cddfSDavid du Colombier }; 967dd7cddfSDavid du Colombier 97ed250ae1SDavid du Colombier // error bound of last sample 98ed250ae1SDavid du Colombier ulong ε; 99ed250ae1SDavid du Colombier 10080ee5cbfSDavid du Colombier static void addntpserver(char *name); 10180ee5cbfSDavid du Colombier static int adjustperiod(vlong diff, vlong accuracy, int secs); 10295fdf19cSDavid du Colombier static void background(void); 10380ee5cbfSDavid du Colombier static int caperror(vlong dhz, int tsecs, vlong taccuracy); 10480ee5cbfSDavid du Colombier static long fstime(void); 10580ee5cbfSDavid du Colombier static int gettime(vlong *nsec, uvlong *ticks, uvlong *hz); // returns time, ticks, hz 10667031067SDavid du Colombier static int getclockprecision(vlong); 10780ee5cbfSDavid du Colombier static void hnputts(void *p, vlong nsec); 10880ee5cbfSDavid du Colombier static void hnputts(void *p, vlong nsec); 1097dd7cddfSDavid du Colombier static void inittime(void); 11080ee5cbfSDavid du Colombier static vlong nhgetts(void *p); 11180ee5cbfSDavid du Colombier static vlong nhgetts(void *p); 1129a747e4fSDavid du Colombier static void ntpserver(char*); 11380ee5cbfSDavid du Colombier static vlong ntpsample(void); 11480ee5cbfSDavid du Colombier static int ntptimediff(NTPserver *ns); 11580ee5cbfSDavid du Colombier static int openfreqfile(void); 11680ee5cbfSDavid du Colombier static vlong readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz); 11780ee5cbfSDavid du Colombier static long rtctime(void); 11880ee5cbfSDavid du Colombier static vlong sample(long (*get)(void)); 1197dd7cddfSDavid du Colombier static void setpriority(void); 12080ee5cbfSDavid du Colombier static void setrootid(char *d); 12180ee5cbfSDavid du Colombier static void settime(vlong now, uvlong hz, vlong delta, int n); // set time, hz, delta, period 1225fab9909SDavid du Colombier static vlong utcsample(void); 12380ee5cbfSDavid du Colombier static uvlong vabs(vlong); 12480ee5cbfSDavid du Colombier static uvlong whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period); 12580ee5cbfSDavid du Colombier static void writefreqfile(int fd, vlong hz, int secs, vlong diff); 1267dd7cddfSDavid du Colombier 1277dd7cddfSDavid du Colombier // ((1970-1900)*365 + 17/*leap days*/)*24*60*60 1287dd7cddfSDavid du Colombier #define EPOCHDIFF 2208988800UL 1297dd7cddfSDavid du Colombier 1307dd7cddfSDavid du Colombier void 1317dd7cddfSDavid du Colombier main(int argc, char **argv) 1327dd7cddfSDavid du Colombier { 13380ee5cbfSDavid du Colombier int i; 13480ee5cbfSDavid du Colombier int secs; // sampling period 13580ee5cbfSDavid du Colombier int tsecs; // temporary sampling period 1367dd7cddfSDavid du Colombier int t, fd; 1377dd7cddfSDavid du Colombier Sample *s, *x, *first, **l; 13880ee5cbfSDavid du Colombier vlong diff, accuracy, taccuracy; 13995fdf19cSDavid du Colombier uvlong hz, minhz, maxhz, period, nhz; 1409a747e4fSDavid du Colombier char *servenet[4]; 1419a747e4fSDavid du Colombier int nservenet; 1427dd7cddfSDavid du Colombier char *a; 1437dd7cddfSDavid du Colombier Tm tl, tg; 1447dd7cddfSDavid du Colombier 14580ee5cbfSDavid du Colombier type = Fs; // by default, sync with the file system 1467dd7cddfSDavid du Colombier debug = 0; 1477dd7cddfSDavid du Colombier accuracy = 1000000LL; // default accuracy is 1 millisecond 1489a747e4fSDavid du Colombier nservenet = 0; 14980ee5cbfSDavid du Colombier tsecs = secs = MinSampleSecs; 1505fab9909SDavid du Colombier timeserver = ""; 1517dd7cddfSDavid du Colombier 1527dd7cddfSDavid du Colombier ARGBEGIN{ 1537dd7cddfSDavid du Colombier case 'a': 1547dd7cddfSDavid du Colombier a = ARGF(); 1557dd7cddfSDavid du Colombier if(a == nil) 1567dd7cddfSDavid du Colombier sysfatal("bad accuracy specified"); 1577dd7cddfSDavid du Colombier accuracy = strtoll(a, 0, 0); // accuracy specified in ns 1587dd7cddfSDavid du Colombier if(accuracy <= 1LL) 1597dd7cddfSDavid du Colombier sysfatal("bad accuracy specified"); 1607dd7cddfSDavid du Colombier break; 1617dd7cddfSDavid du Colombier case 'f': 1627dd7cddfSDavid du Colombier type = Fs; 1637dd7cddfSDavid du Colombier stratum = 2; 1647dd7cddfSDavid du Colombier break; 1657dd7cddfSDavid du Colombier case 'r': 1667dd7cddfSDavid du Colombier type = Rtc; 16780ee5cbfSDavid du Colombier stratum = 0; 1687dd7cddfSDavid du Colombier break; 1695fab9909SDavid du Colombier case 'U': 1705fab9909SDavid du Colombier type = Utc; 1715fab9909SDavid du Colombier stratum = 1; 1725fab9909SDavid du Colombier timeserver = ARGF(); 1735fab9909SDavid du Colombier if (timeserver == nil) 1745fab9909SDavid du Colombier sysfatal("bad time source"); 1755fab9909SDavid du Colombier break; 1767dd7cddfSDavid du Colombier case 'n': 1777dd7cddfSDavid du Colombier type = Ntp; 1787dd7cddfSDavid du Colombier break; 1797dd7cddfSDavid du Colombier case 'D': 1807dd7cddfSDavid du Colombier debug = 1; 1817dd7cddfSDavid du Colombier break; 1827dd7cddfSDavid du Colombier case 'd': 1837dd7cddfSDavid du Colombier dir = ARGF(); 1847dd7cddfSDavid du Colombier break; 1857dd7cddfSDavid du Colombier case 'L': 1867dd7cddfSDavid du Colombier // 1877dd7cddfSDavid du Colombier // Assume time source in local time rather than GMT. 1887dd7cddfSDavid du Colombier // Calculate difference so that rtctime can return GMT. 1897dd7cddfSDavid du Colombier // This is useful with the rtc on PC's that run Windows 1907dd7cddfSDavid du Colombier // since Windows keeps the local time in the rtc. 1917dd7cddfSDavid du Colombier // 1927dd7cddfSDavid du Colombier t = time(0); 1937dd7cddfSDavid du Colombier tl = *localtime(t); 1947dd7cddfSDavid du Colombier tg = *gmtime(t); 1957dd7cddfSDavid du Colombier 1967dd7cddfSDavid du Colombier // if the years are different, we're at most a day off, so just rewrite 1977dd7cddfSDavid du Colombier if(tl.year < tg.year){ 1987dd7cddfSDavid du Colombier tg.year--; 1997dd7cddfSDavid du Colombier tg.yday = tl.yday + 1; 2007dd7cddfSDavid du Colombier }else if(tl.year > tg.year){ 2017dd7cddfSDavid du Colombier tl.year--; 2027dd7cddfSDavid du Colombier tl.yday = tg.yday+1; 2037dd7cddfSDavid du Colombier } 2047dd7cddfSDavid du Colombier assert(tl.year == tg.year); 2057dd7cddfSDavid du Colombier 2067dd7cddfSDavid du Colombier tg.sec -= tl.sec; 2077dd7cddfSDavid du Colombier tg.min -= tl.min; 2087dd7cddfSDavid du Colombier tg.hour -= tl.hour; 2097dd7cddfSDavid du Colombier tg.yday -= tl.yday; 2107dd7cddfSDavid du Colombier gmtdelta = tg.sec+60*(tg.min+60*(tg.hour+tg.yday*24)); 2117dd7cddfSDavid du Colombier 2127dd7cddfSDavid du Colombier assert(abs(gmtdelta) <= 24*60*60); 2137dd7cddfSDavid du Colombier break; 2147dd7cddfSDavid du Colombier case 'i': 2157dd7cddfSDavid du Colombier impotent = 1; 2167dd7cddfSDavid du Colombier break; 2175fab9909SDavid du Colombier case 'I': 2185fab9909SDavid du Colombier Rootid = ARGF(); 2195fab9909SDavid du Colombier break; 22080ee5cbfSDavid du Colombier case 's': 2219a747e4fSDavid du Colombier if(nservenet >= nelem(servenet)) 2229a747e4fSDavid du Colombier sysfatal("too many networks to serve on"); 2239a747e4fSDavid du Colombier a = ARGF(); 2249a747e4fSDavid du Colombier if(a == nil) 2259a747e4fSDavid du Colombier sysfatal("must specify network to serve on"); 2269a747e4fSDavid du Colombier servenet[nservenet++] = a; 22780ee5cbfSDavid du Colombier break; 2287dd7cddfSDavid du Colombier case 'l': 2297dd7cddfSDavid du Colombier logging = 1; 2307dd7cddfSDavid du Colombier break; 2313ff48bf5SDavid du Colombier case 'S': 2323ff48bf5SDavid du Colombier a = ARGF(); 2333ff48bf5SDavid du Colombier if(a == nil) 2343ff48bf5SDavid du Colombier sysfatal("bad stratum specified"); 2353ff48bf5SDavid du Colombier stratum = strtoll(a, 0, 0); 2363ff48bf5SDavid du Colombier break; 2377dd7cddfSDavid du Colombier }ARGEND; 2387dd7cddfSDavid du Colombier 2399a747e4fSDavid du Colombier fmtinstall('E', eipfmt); 2409a747e4fSDavid du Colombier fmtinstall('I', eipfmt); 2419a747e4fSDavid du Colombier fmtinstall('V', eipfmt); 24280ee5cbfSDavid du Colombier sysid = getenv("sysname"); 2437dd7cddfSDavid du Colombier 24495fdf19cSDavid du Colombier // detach from the current namespace 24595fdf19cSDavid du Colombier if(debug) 24695fdf19cSDavid du Colombier rfork(RFNAMEG); 24795fdf19cSDavid du Colombier 24880ee5cbfSDavid du Colombier switch(type){ 24980ee5cbfSDavid du Colombier case Fs: 2507dd7cddfSDavid du Colombier if(argc > 0) 2517dd7cddfSDavid du Colombier timeserver = argv[0]; 2527dd7cddfSDavid du Colombier else 2537dd7cddfSDavid du Colombier timeserver = "/srv/boot"; 2547dd7cddfSDavid du Colombier break; 2557dd7cddfSDavid du Colombier case Ntp: 25680ee5cbfSDavid du Colombier if(argc > 0){ 25780ee5cbfSDavid du Colombier for(i = 0; i <argc; i++) 25880ee5cbfSDavid du Colombier addntpserver(argv[i]); 2599a747e4fSDavid du Colombier } else { 26080ee5cbfSDavid du Colombier addntpserver("$ntp"); 2619a747e4fSDavid du Colombier } 2627dd7cddfSDavid du Colombier break; 2637dd7cddfSDavid du Colombier } 2647dd7cddfSDavid du Colombier 2657dd7cddfSDavid du Colombier setpriority(); 2667dd7cddfSDavid du Colombier 26780ee5cbfSDavid du Colombier // figure out our time interface and initial frequency 2687dd7cddfSDavid du Colombier inittime(); 2697dd7cddfSDavid du Colombier gettime(0, 0, &hz); 270*be93767fSDavid du Colombier minhz = hz/10; 271*be93767fSDavid du Colombier maxhz = hz*10; 27267031067SDavid du Colombier myprec = getclockprecision(hz); 2737dd7cddfSDavid du Colombier 27480ee5cbfSDavid du Colombier // convert the accuracy from nanoseconds to ticks 27580ee5cbfSDavid du Colombier taccuracy = hz*accuracy/SEC; 2767dd7cddfSDavid du Colombier 2777dd7cddfSDavid du Colombier // 2787dd7cddfSDavid du Colombier // bind in clocks 2797dd7cddfSDavid du Colombier // 2807dd7cddfSDavid du Colombier switch(type){ 2817dd7cddfSDavid du Colombier case Fs: 2827dd7cddfSDavid du Colombier fd = open(timeserver, ORDWR); 2837dd7cddfSDavid du Colombier if(fd < 0) 2847dd7cddfSDavid du Colombier sysfatal("opening %s: %r\n", timeserver); 2855d459b5aSDavid du Colombier if(amount(fd, "/n/boot", MREPL, "") < 0) 2867dd7cddfSDavid du Colombier sysfatal("mounting %s: %r\n", timeserver); 2877dd7cddfSDavid du Colombier close(fd); 2887dd7cddfSDavid du Colombier break; 2897dd7cddfSDavid du Colombier case Rtc: 2907dd7cddfSDavid du Colombier bind("#r", "/dev", MAFTER); 2917dd7cddfSDavid du Colombier if(access("/dev/rtc", AREAD) < 0) 2927dd7cddfSDavid du Colombier sysfatal("accessing /dev/rtc: %r\n"); 2937dd7cddfSDavid du Colombier break; 2945fab9909SDavid du Colombier case Utc: 2955fab9909SDavid du Colombier fd = open(timeserver, OREAD); 2965fab9909SDavid du Colombier if(fd < 0) 2975fab9909SDavid du Colombier sysfatal("opening %s: %r\n", timeserver); 2985fab9909SDavid du Colombier utcfil = fd; 2995fab9909SDavid du Colombier break; 3007dd7cddfSDavid du Colombier } 3017dd7cddfSDavid du Colombier 3027dd7cddfSDavid du Colombier // 3039a747e4fSDavid du Colombier // start a local ntp server(s) 3047dd7cddfSDavid du Colombier // 3059a747e4fSDavid du Colombier for(i = 0; i < nservenet; i++){ 3067dd7cddfSDavid du Colombier switch(rfork(RFPROC|RFFDG|RFMEM|RFNOWAIT)){ 3077dd7cddfSDavid du Colombier case -1: 3087dd7cddfSDavid du Colombier sysfatal("forking: %r"); 3097dd7cddfSDavid du Colombier break; 3107dd7cddfSDavid du Colombier case 0: 3119a747e4fSDavid du Colombier ntpserver(servenet[i]); 3127dd7cddfSDavid du Colombier _exits(0); 3137dd7cddfSDavid du Colombier break; 3147dd7cddfSDavid du Colombier default: 3157dd7cddfSDavid du Colombier break; 3167dd7cddfSDavid du Colombier } 3177dd7cddfSDavid du Colombier } 3187dd7cddfSDavid du Colombier 3197dd7cddfSDavid du Colombier // get the last known frequency from the file 32080ee5cbfSDavid du Colombier fd = openfreqfile(); 3217dd7cddfSDavid du Colombier hz = readfreqfile(fd, hz, minhz, maxhz); 3227dd7cddfSDavid du Colombier 32380ee5cbfSDavid du Colombier // this is the main loop. it gets a sample, adjusts the 32480ee5cbfSDavid du Colombier // clock and computes a sleep period until the next loop. 32580ee5cbfSDavid du Colombier // we balance frequency drift against the length of the 32680ee5cbfSDavid du Colombier // period to avoid blowing the accuracy limit. 3277dd7cddfSDavid du Colombier first = nil; 3287dd7cddfSDavid du Colombier l = &first; 32980ee5cbfSDavid du Colombier avgerr = accuracy>>1; 33095fdf19cSDavid du Colombier for(;; background(),sleep(tsecs*(1000))){ 3317dd7cddfSDavid du Colombier s = mallocz(sizeof(*s), 1); 3327dd7cddfSDavid du Colombier diff = 0; 3337dd7cddfSDavid du Colombier 3347dd7cddfSDavid du Colombier // get times for this sample 335ed250ae1SDavid du Colombier ε = ~0; 3367dd7cddfSDavid du Colombier switch(type){ 3377dd7cddfSDavid du Colombier case Fs: 3387dd7cddfSDavid du Colombier s->stime = sample(fstime); 3397dd7cddfSDavid du Colombier break; 3407dd7cddfSDavid du Colombier case Rtc: 3417dd7cddfSDavid du Colombier s->stime = sample(rtctime); 3427dd7cddfSDavid du Colombier break; 3435fab9909SDavid du Colombier case Utc: 3445fab9909SDavid du Colombier s->stime = utcsample(); 3455fab9909SDavid du Colombier if(s->stime == 0LL){ 3465fab9909SDavid du Colombier if(logging) 3475fab9909SDavid du Colombier syslog(0, logfile, "no sample"); 3485fab9909SDavid du Colombier free(s); 3495fab9909SDavid du Colombier if (secs > 60 * 15) 3505fab9909SDavid du Colombier tsecs = 60*15; 3515fab9909SDavid du Colombier continue; 3525fab9909SDavid du Colombier } 3535fab9909SDavid du Colombier break; 3547dd7cddfSDavid du Colombier case Ntp: 35580ee5cbfSDavid du Colombier diff = ntpsample(); 35680ee5cbfSDavid du Colombier if(diff == 0LL){ 35780ee5cbfSDavid du Colombier if(logging) 35880ee5cbfSDavid du Colombier syslog(0, logfile, "no sample"); 35980ee5cbfSDavid du Colombier free(s); 36080ee5cbfSDavid du Colombier if(secs > 60*15) 36180ee5cbfSDavid du Colombier tsecs = 60*15; 36280ee5cbfSDavid du Colombier continue; 36380ee5cbfSDavid du Colombier } 3647dd7cddfSDavid du Colombier break; 3657dd7cddfSDavid du Colombier } 3667dd7cddfSDavid du Colombier 3677dd7cddfSDavid du Colombier // use fastest method to read local clock and ticks 3687dd7cddfSDavid du Colombier gettime(&s->ltime, &s->ticks, 0); 3697dd7cddfSDavid du Colombier if(type == Ntp) 3707dd7cddfSDavid du Colombier s->stime = s->ltime + diff; 3717dd7cddfSDavid du Colombier 372940a793aSDavid du Colombier // if the sample was bad, ignore it 373940a793aSDavid du Colombier if(s->stime < 0){ 37480ee5cbfSDavid du Colombier free(s); 37580ee5cbfSDavid du Colombier continue; 37680ee5cbfSDavid du Colombier } 37780ee5cbfSDavid du Colombier 37880ee5cbfSDavid du Colombier // reset local time 37980ee5cbfSDavid du Colombier diff = s->stime - s->ltime; 38080ee5cbfSDavid du Colombier if(diff > 10*SEC || diff < -10*SEC){ 38180ee5cbfSDavid du Colombier // we're way off, just set the time 38280ee5cbfSDavid du Colombier secs = MinSampleSecs; 38380ee5cbfSDavid du Colombier settime(s->stime, 0, 0, 0); 38480ee5cbfSDavid du Colombier } else { 38580ee5cbfSDavid du Colombier // keep a running average of the error. 38680ee5cbfSDavid du Colombier avgerr = (avgerr>>1) + (vabs(diff)>>1); 38780ee5cbfSDavid du Colombier 38880ee5cbfSDavid du Colombier // the time to next sample depends on how good or 38980ee5cbfSDavid du Colombier // bad we're doing. 39080ee5cbfSDavid du Colombier tsecs = secs = adjustperiod(diff, accuracy, secs); 39180ee5cbfSDavid du Colombier 39280ee5cbfSDavid du Colombier // work off the fixed difference. This is done 39380ee5cbfSDavid du Colombier // by adding a ramp to the clock. Each 100th of a 39480ee5cbfSDavid du Colombier // second (or so) the kernel will add diff/(4*secs*100) 39580ee5cbfSDavid du Colombier // to the clock. we only do 1/4 of the difference per 39680ee5cbfSDavid du Colombier // period to dampen any measurement noise. 397ed250ae1SDavid du Colombier // 398ed250ae1SDavid du Colombier // any difference greater than ε we work off during the 399ed250ae1SDavid du Colombier // sampling period. 400ed250ae1SDavid du Colombier if(abs(diff) > ε){ 401ed250ae1SDavid du Colombier if(diff > 0) 402ed250ae1SDavid du Colombier settime(-1, 0, diff-((3*ε)/4), secs); 403ed250ae1SDavid du Colombier else 404ed250ae1SDavid du Colombier settime(-1, 0, diff+((3*ε)/4), secs); 405ed250ae1SDavid du Colombier } else 40680ee5cbfSDavid du Colombier settime(-1, 0, diff, 4*secs); 40780ee5cbfSDavid du Colombier 40880ee5cbfSDavid du Colombier } 40980ee5cbfSDavid du Colombier if(debug) 41080ee5cbfSDavid du Colombier fprint(2, "δ %lld avgδ %lld f %lld\n", diff, avgerr, hz); 41180ee5cbfSDavid du Colombier 41280ee5cbfSDavid du Colombier // dump old samples (keep at least one) 4137dd7cddfSDavid du Colombier while(first != nil){ 4147dd7cddfSDavid du Colombier if(first->next == nil) 4157dd7cddfSDavid du Colombier break; 41680ee5cbfSDavid du Colombier if(s->stime - first->next->stime < DAY) 4177dd7cddfSDavid du Colombier break; 4187dd7cddfSDavid du Colombier x = first; 41980ee5cbfSDavid du Colombier first = first->next; 4207dd7cddfSDavid du Colombier free(x); 4217dd7cddfSDavid du Colombier } 4227dd7cddfSDavid du Colombier 42380ee5cbfSDavid du Colombier // The sampling error is limited by the total error. If 42480ee5cbfSDavid du Colombier // we make sure the sampling period is at least 16 million 42580ee5cbfSDavid du Colombier // times the average error, we should calculate a frequency 42680ee5cbfSDavid du Colombier // with on average a 1e-7 error. 42780ee5cbfSDavid du Colombier // 42880ee5cbfSDavid du Colombier // So that big hz changes don't blow our accuracy requirement, 42980ee5cbfSDavid du Colombier // we shorten the period to make sure that δhz*secs will be 43080ee5cbfSDavid du Colombier // greater than the accuracy limit. 43180ee5cbfSDavid du Colombier period = avgerr<<24; 43280ee5cbfSDavid du Colombier for(x = first; x != nil; x = x->next){ 43380ee5cbfSDavid du Colombier if(s->stime - x->stime < period) 43480ee5cbfSDavid du Colombier break; 43580ee5cbfSDavid du Colombier if(x->next == nil || s->stime - x->next->stime < period) 43680ee5cbfSDavid du Colombier break; 43780ee5cbfSDavid du Colombier } 43880ee5cbfSDavid du Colombier if(x != nil){ 43980ee5cbfSDavid du Colombier nhz = whatisthefrequencykenneth( 44080ee5cbfSDavid du Colombier hz, minhz, maxhz, 44180ee5cbfSDavid du Colombier s->stime - x->stime, 44280ee5cbfSDavid du Colombier s->ticks - x->ticks, 44380ee5cbfSDavid du Colombier period); 44480ee5cbfSDavid du Colombier tsecs = caperror(vabs(nhz-hz), tsecs, taccuracy); 44580ee5cbfSDavid du Colombier hz = nhz; 44680ee5cbfSDavid du Colombier writefreqfile(fd, hz, (s->stime - x->stime)/SEC, diff); 4477dd7cddfSDavid du Colombier } 4487dd7cddfSDavid du Colombier 44980ee5cbfSDavid du Colombier // add current sample to list. 4507dd7cddfSDavid du Colombier *l = s; 4517dd7cddfSDavid du Colombier l = &s->next; 45280ee5cbfSDavid du Colombier 45380ee5cbfSDavid du Colombier if(logging) 45480ee5cbfSDavid du Colombier syslog(0, logfile, "δ %lld avgδ %lld hz %lld", 45580ee5cbfSDavid du Colombier diff, avgerr, hz); 4567dd7cddfSDavid du Colombier } 4577dd7cddfSDavid du Colombier } 4587dd7cddfSDavid du Colombier 45980ee5cbfSDavid du Colombier // 46080ee5cbfSDavid du Colombier // adjust the sampling period with some histeresis 46180ee5cbfSDavid du Colombier // 46280ee5cbfSDavid du Colombier static int 46380ee5cbfSDavid du Colombier adjustperiod(vlong diff, vlong accuracy, int secs) 46480ee5cbfSDavid du Colombier { 46580ee5cbfSDavid du Colombier uvlong absdiff; 46680ee5cbfSDavid du Colombier 46780ee5cbfSDavid du Colombier absdiff = vabs(diff); 46880ee5cbfSDavid du Colombier 46980ee5cbfSDavid du Colombier if(absdiff < (accuracy>>1)) 47080ee5cbfSDavid du Colombier secs += 60; 47180ee5cbfSDavid du Colombier else if(absdiff > accuracy) 47280ee5cbfSDavid du Colombier secs >>= 1; 47380ee5cbfSDavid du Colombier else 47480ee5cbfSDavid du Colombier secs -= 60; 47580ee5cbfSDavid du Colombier if(secs < MinSampleSecs) 47680ee5cbfSDavid du Colombier secs = MinSampleSecs; 47780ee5cbfSDavid du Colombier return secs; 47880ee5cbfSDavid du Colombier } 47980ee5cbfSDavid du Colombier 48080ee5cbfSDavid du Colombier // 48180ee5cbfSDavid du Colombier // adjust the frequency 48280ee5cbfSDavid du Colombier // 48380ee5cbfSDavid du Colombier static uvlong 48480ee5cbfSDavid du Colombier whatisthefrequencykenneth(uvlong hz, uvlong minhz, uvlong maxhz, vlong dt, vlong ticks, vlong period) 48580ee5cbfSDavid du Colombier { 48680ee5cbfSDavid du Colombier static mpint *mpdt; 48780ee5cbfSDavid du Colombier static mpint *mpticks; 48880ee5cbfSDavid du Colombier static mpint *mphz; 48980ee5cbfSDavid du Colombier static mpint *mpbillion; 49080ee5cbfSDavid du Colombier uvlong ohz = hz; 49180ee5cbfSDavid du Colombier 49280ee5cbfSDavid du Colombier // sanity check 49380ee5cbfSDavid du Colombier if(dt <= 0 || ticks <= 0) 49480ee5cbfSDavid du Colombier return hz; 49580ee5cbfSDavid du Colombier 49680ee5cbfSDavid du Colombier if(mphz == nil){ 49780ee5cbfSDavid du Colombier mphz = mpnew(0); 49880ee5cbfSDavid du Colombier mpbillion = uvtomp(SEC, nil); 49980ee5cbfSDavid du Colombier } 50080ee5cbfSDavid du Colombier 50180ee5cbfSDavid du Colombier // hz = (ticks*SEC)/dt 50280ee5cbfSDavid du Colombier mpdt = vtomp(dt, mpdt); 50380ee5cbfSDavid du Colombier mpticks = vtomp(ticks, mpticks); 50480ee5cbfSDavid du Colombier mpmul(mpticks, mpbillion, mpticks); 50580ee5cbfSDavid du Colombier mpdiv(mpticks, mpdt, mphz, nil); 50680ee5cbfSDavid du Colombier hz = mptoui(mphz); 50780ee5cbfSDavid du Colombier 50880ee5cbfSDavid du Colombier // sanity 50980ee5cbfSDavid du Colombier if(hz < minhz || hz > maxhz) 51080ee5cbfSDavid du Colombier return ohz; 51180ee5cbfSDavid du Colombier 51280ee5cbfSDavid du Colombier // damp the change if we're shorter than the target period 51380ee5cbfSDavid du Colombier if(period > dt) 51480ee5cbfSDavid du Colombier hz = (12ULL*ohz + 4ULL*hz)/16ULL; 51580ee5cbfSDavid du Colombier 51680ee5cbfSDavid du Colombier settime(-1, hz, 0, 0); 51780ee5cbfSDavid du Colombier return hz; 51880ee5cbfSDavid du Colombier } 51980ee5cbfSDavid du Colombier 52080ee5cbfSDavid du Colombier // We may be changing the frequency to match a bad measurement 52180ee5cbfSDavid du Colombier // or to match a condition no longer in effet. To make sure 52280ee5cbfSDavid du Colombier // that this doesn't blow our error budget over the next measurement 52380ee5cbfSDavid du Colombier // period, shorten the period to make sure that δhz*secs will be 52480ee5cbfSDavid du Colombier // less than the accuracy limit. Here taccuracy is accuracy converted 52580ee5cbfSDavid du Colombier // from nanoseconds to ticks. 52680ee5cbfSDavid du Colombier static int 52780ee5cbfSDavid du Colombier caperror(vlong dhz, int tsecs, vlong taccuracy) 52880ee5cbfSDavid du Colombier { 5299a747e4fSDavid du Colombier if(dhz*tsecs <= taccuracy) 53080ee5cbfSDavid du Colombier return tsecs; 53180ee5cbfSDavid du Colombier 53280ee5cbfSDavid du Colombier if(debug) 53380ee5cbfSDavid du Colombier fprint(2, "δhz %lld tsecs %d tacc %lld\n", dhz, tsecs, taccuracy); 53480ee5cbfSDavid du Colombier 53580ee5cbfSDavid du Colombier tsecs = taccuracy/dhz; 53680ee5cbfSDavid du Colombier if(tsecs < MinSampleSecs) 53780ee5cbfSDavid du Colombier tsecs = MinSampleSecs; 53880ee5cbfSDavid du Colombier return tsecs; 53980ee5cbfSDavid du Colombier } 5407dd7cddfSDavid du Colombier 5417dd7cddfSDavid du Colombier // 5427dd7cddfSDavid du Colombier // kernel interface 5437dd7cddfSDavid du Colombier // 5447dd7cddfSDavid du Colombier enum 5457dd7cddfSDavid du Colombier { 5467dd7cddfSDavid du Colombier Ibintime, 5477dd7cddfSDavid du Colombier Insec, 5487dd7cddfSDavid du Colombier Itiming, 5497dd7cddfSDavid du Colombier }; 5507dd7cddfSDavid du Colombier int ifc; 5517dd7cddfSDavid du Colombier int bintimefd = -1; 5527dd7cddfSDavid du Colombier int timingfd = -1; 5537dd7cddfSDavid du Colombier int nsecfd = -1; 5547dd7cddfSDavid du Colombier int fastclockfd = -1; 5557dd7cddfSDavid du Colombier 5567dd7cddfSDavid du Colombier static void 5577dd7cddfSDavid du Colombier inittime(void) 5587dd7cddfSDavid du Colombier { 5597dd7cddfSDavid du Colombier int mode; 5607dd7cddfSDavid du Colombier 5617dd7cddfSDavid du Colombier if(impotent) 5627dd7cddfSDavid du Colombier mode = OREAD; 5637dd7cddfSDavid du Colombier else 5647dd7cddfSDavid du Colombier mode = ORDWR; 5657dd7cddfSDavid du Colombier 5667dd7cddfSDavid du Colombier // bind in clocks 5677dd7cddfSDavid du Colombier if(access("/dev/time", 0) < 0) 5687dd7cddfSDavid du Colombier bind("#c", "/dev", MAFTER); 5697dd7cddfSDavid du Colombier if(access("/dev/rtc", 0) < 0) 5707dd7cddfSDavid du Colombier bind("#r", "/dev", MAFTER); 5717dd7cddfSDavid du Colombier 5727dd7cddfSDavid du Colombier // figure out what interface we have 5737dd7cddfSDavid du Colombier ifc = Ibintime; 5747dd7cddfSDavid du Colombier bintimefd = open("/dev/bintime", mode); 5757dd7cddfSDavid du Colombier if(bintimefd >= 0) 5767dd7cddfSDavid du Colombier return; 5777dd7cddfSDavid du Colombier ifc = Insec; 5787dd7cddfSDavid du Colombier nsecfd = open("/dev/nsec", mode); 5797dd7cddfSDavid du Colombier if(nsecfd < 0) 5807dd7cddfSDavid du Colombier sysfatal("opening /dev/nsec"); 5817dd7cddfSDavid du Colombier fastclockfd = open("/dev/fastclock", mode); 5827dd7cddfSDavid du Colombier if(fastclockfd < 0) 5837dd7cddfSDavid du Colombier sysfatal("opening /dev/fastclock"); 5847dd7cddfSDavid du Colombier timingfd = open("/dev/timing", OREAD); 5857dd7cddfSDavid du Colombier if(timingfd < 0) 5867dd7cddfSDavid du Colombier return; 5877dd7cddfSDavid du Colombier ifc = Itiming; 5887dd7cddfSDavid du Colombier } 5897dd7cddfSDavid du Colombier 5907dd7cddfSDavid du Colombier // 5917dd7cddfSDavid du Colombier // convert binary numbers from/to kernel 5927dd7cddfSDavid du Colombier // 5937dd7cddfSDavid du Colombier static uvlong uvorder = 0x0001020304050607ULL; 5947dd7cddfSDavid du Colombier 5957dd7cddfSDavid du Colombier static uchar* 5967dd7cddfSDavid du Colombier be2vlong(vlong *to, uchar *f) 5977dd7cddfSDavid du Colombier { 5987dd7cddfSDavid du Colombier uchar *t, *o; 5997dd7cddfSDavid du Colombier int i; 6007dd7cddfSDavid du Colombier 6017dd7cddfSDavid du Colombier t = (uchar*)to; 6027dd7cddfSDavid du Colombier o = (uchar*)&uvorder; 6037dd7cddfSDavid du Colombier for(i = 0; i < sizeof(vlong); i++) 6047dd7cddfSDavid du Colombier t[o[i]] = f[i]; 6057dd7cddfSDavid du Colombier return f+sizeof(vlong); 6067dd7cddfSDavid du Colombier } 6077dd7cddfSDavid du Colombier 6087dd7cddfSDavid du Colombier static uchar* 6097dd7cddfSDavid du Colombier vlong2be(uchar *t, vlong from) 6107dd7cddfSDavid du Colombier { 6117dd7cddfSDavid du Colombier uchar *f, *o; 6127dd7cddfSDavid du Colombier int i; 6137dd7cddfSDavid du Colombier 6147dd7cddfSDavid du Colombier f = (uchar*)&from; 6157dd7cddfSDavid du Colombier o = (uchar*)&uvorder; 6167dd7cddfSDavid du Colombier for(i = 0; i < sizeof(vlong); i++) 6177dd7cddfSDavid du Colombier t[i] = f[o[i]]; 6187dd7cddfSDavid du Colombier return t+sizeof(vlong); 6197dd7cddfSDavid du Colombier } 6207dd7cddfSDavid du Colombier 6217dd7cddfSDavid du Colombier static long order = 0x00010203; 6227dd7cddfSDavid du Colombier 6237dd7cddfSDavid du Colombier static uchar* 6247dd7cddfSDavid du Colombier be2long(long *to, uchar *f) 6257dd7cddfSDavid du Colombier { 6267dd7cddfSDavid du Colombier uchar *t, *o; 6277dd7cddfSDavid du Colombier int i; 6287dd7cddfSDavid du Colombier 6297dd7cddfSDavid du Colombier t = (uchar*)to; 6307dd7cddfSDavid du Colombier o = (uchar*)ℴ 6317dd7cddfSDavid du Colombier for(i = 0; i < sizeof(long); i++) 6327dd7cddfSDavid du Colombier t[o[i]] = f[i]; 6337dd7cddfSDavid du Colombier return f+sizeof(long); 6347dd7cddfSDavid du Colombier } 6357dd7cddfSDavid du Colombier 6367dd7cddfSDavid du Colombier static uchar* 6377dd7cddfSDavid du Colombier long2be(uchar *t, long from) 6387dd7cddfSDavid du Colombier { 6397dd7cddfSDavid du Colombier uchar *f, *o; 6407dd7cddfSDavid du Colombier int i; 6417dd7cddfSDavid du Colombier 6427dd7cddfSDavid du Colombier f = (uchar*)&from; 6437dd7cddfSDavid du Colombier o = (uchar*)ℴ 6447dd7cddfSDavid du Colombier for(i = 0; i < sizeof(long); i++) 6457dd7cddfSDavid du Colombier t[i] = f[o[i]]; 6467dd7cddfSDavid du Colombier return t+sizeof(long); 6477dd7cddfSDavid du Colombier } 6487dd7cddfSDavid du Colombier 6497dd7cddfSDavid du Colombier // 6507dd7cddfSDavid du Colombier // read ticks and local time in nanoseconds 6517dd7cddfSDavid du Colombier // 6527dd7cddfSDavid du Colombier static int 6537dd7cddfSDavid du Colombier gettime(vlong *nsec, uvlong *ticks, uvlong *hz) 6547dd7cddfSDavid du Colombier { 6557dd7cddfSDavid du Colombier int i, n; 6567dd7cddfSDavid du Colombier uchar ub[3*8], *p; 6577dd7cddfSDavid du Colombier char b[2*24+1]; 6587dd7cddfSDavid du Colombier 6597dd7cddfSDavid du Colombier switch(ifc){ 6607dd7cddfSDavid du Colombier case Ibintime: 6617dd7cddfSDavid du Colombier n = sizeof(vlong); 6627dd7cddfSDavid du Colombier if(hz != nil) 6637dd7cddfSDavid du Colombier n = 3*sizeof(vlong); 6647dd7cddfSDavid du Colombier if(ticks != nil) 6657dd7cddfSDavid du Colombier n = 2*sizeof(vlong); 6667dd7cddfSDavid du Colombier i = read(bintimefd, ub, n); 6677dd7cddfSDavid du Colombier if(i != n) 6687dd7cddfSDavid du Colombier break; 6697dd7cddfSDavid du Colombier p = ub; 6707dd7cddfSDavid du Colombier if(nsec != nil) 6717dd7cddfSDavid du Colombier be2vlong(nsec, ub); 6727dd7cddfSDavid du Colombier p += sizeof(vlong); 6737dd7cddfSDavid du Colombier if(ticks != nil) 6747dd7cddfSDavid du Colombier be2vlong((vlong*)ticks, p); 6757dd7cddfSDavid du Colombier p += sizeof(vlong); 6767dd7cddfSDavid du Colombier if(hz != nil) 6777dd7cddfSDavid du Colombier be2vlong((vlong*)hz, p); 6787dd7cddfSDavid du Colombier return 0; 6797dd7cddfSDavid du Colombier case Itiming: 6807dd7cddfSDavid du Colombier n = sizeof(vlong); 6817dd7cddfSDavid du Colombier if(ticks != nil) 6827dd7cddfSDavid du Colombier n = 2*sizeof(vlong); 6837dd7cddfSDavid du Colombier i = read(timingfd, ub, n); 6847dd7cddfSDavid du Colombier if(i != n) 6857dd7cddfSDavid du Colombier break; 6867dd7cddfSDavid du Colombier p = ub; 6877dd7cddfSDavid du Colombier if(nsec != nil) 6887dd7cddfSDavid du Colombier be2vlong(nsec, ub); 6897dd7cddfSDavid du Colombier p += sizeof(vlong); 6907dd7cddfSDavid du Colombier if(ticks != nil) 6917dd7cddfSDavid du Colombier be2vlong((vlong*)ticks, p); 6927dd7cddfSDavid du Colombier if(hz != nil){ 6937dd7cddfSDavid du Colombier seek(fastclockfd, 0, 0); 6947dd7cddfSDavid du Colombier n = read(fastclockfd, b, sizeof(b)-1); 6957dd7cddfSDavid du Colombier if(n <= 0) 6967dd7cddfSDavid du Colombier break; 6977dd7cddfSDavid du Colombier b[n] = 0; 6987dd7cddfSDavid du Colombier *hz = strtoll(b+24, 0, 0); 6997dd7cddfSDavid du Colombier } 7007dd7cddfSDavid du Colombier return 0; 7017dd7cddfSDavid du Colombier case Insec: 7027dd7cddfSDavid du Colombier if(nsec != nil){ 7037dd7cddfSDavid du Colombier seek(nsecfd, 0, 0); 7047dd7cddfSDavid du Colombier n = read(nsecfd, b, sizeof(b)-1); 7057dd7cddfSDavid du Colombier if(n <= 0) 7067dd7cddfSDavid du Colombier break; 7077dd7cddfSDavid du Colombier b[n] = 0; 7087dd7cddfSDavid du Colombier *nsec = strtoll(b, 0, 0); 7097dd7cddfSDavid du Colombier } 7107dd7cddfSDavid du Colombier if(ticks != nil){ 7117dd7cddfSDavid du Colombier seek(fastclockfd, 0, 0); 7127dd7cddfSDavid du Colombier n = read(fastclockfd, b, sizeof(b)-1); 7137dd7cddfSDavid du Colombier if(n <= 0) 7147dd7cddfSDavid du Colombier break; 7157dd7cddfSDavid du Colombier b[n] = 0; 7167dd7cddfSDavid du Colombier *ticks = strtoll(b, 0, 0); 7177dd7cddfSDavid du Colombier } 7187dd7cddfSDavid du Colombier if(hz != nil){ 7197dd7cddfSDavid du Colombier seek(fastclockfd, 0, 0); 7207dd7cddfSDavid du Colombier n = read(fastclockfd, b, sizeof(b)-1); 7217dd7cddfSDavid du Colombier if(n <= 24) 7227dd7cddfSDavid du Colombier break; 7237dd7cddfSDavid du Colombier b[n] = 0; 7247dd7cddfSDavid du Colombier *hz = strtoll(b+24, 0, 0); 7257dd7cddfSDavid du Colombier } 7267dd7cddfSDavid du Colombier return 0; 7277dd7cddfSDavid du Colombier } 7287dd7cddfSDavid du Colombier return -1; 7297dd7cddfSDavid du Colombier } 7307dd7cddfSDavid du Colombier 7317dd7cddfSDavid du Colombier static void 7327dd7cddfSDavid du Colombier settime(vlong now, uvlong hz, vlong delta, int n) 7337dd7cddfSDavid du Colombier { 7347dd7cddfSDavid du Colombier uchar b[1+sizeof(vlong)+sizeof(long)], *p; 7357dd7cddfSDavid du Colombier 7367dd7cddfSDavid du Colombier if(debug) 7377dd7cddfSDavid du Colombier fprint(2, "settime(now=%lld, hz=%llud, delta=%lld, period=%d)\n", now, hz, delta, n); 7387dd7cddfSDavid du Colombier if(impotent) 7397dd7cddfSDavid du Colombier return; 7407dd7cddfSDavid du Colombier switch(ifc){ 7417dd7cddfSDavid du Colombier case Ibintime: 7427dd7cddfSDavid du Colombier if(now >= 0){ 7437dd7cddfSDavid du Colombier p = b; 7447dd7cddfSDavid du Colombier *p++ = 'n'; 7457dd7cddfSDavid du Colombier p = vlong2be(p, now); 7467dd7cddfSDavid du Colombier if(write(bintimefd, b, p-b) < 0) 7477dd7cddfSDavid du Colombier sysfatal("writing /dev/bintime: %r"); 7487dd7cddfSDavid du Colombier } 7497dd7cddfSDavid du Colombier if(delta != 0){ 7507dd7cddfSDavid du Colombier p = b; 7517dd7cddfSDavid du Colombier *p++ = 'd'; 7527dd7cddfSDavid du Colombier p = vlong2be(p, delta); 7537dd7cddfSDavid du Colombier p = long2be(p, n); 7547dd7cddfSDavid du Colombier if(write(bintimefd, b, p-b) < 0) 7557dd7cddfSDavid du Colombier sysfatal("writing /dev/bintime: %r"); 7567dd7cddfSDavid du Colombier } 7577dd7cddfSDavid du Colombier if(hz != 0){ 7587dd7cddfSDavid du Colombier p = b; 7597dd7cddfSDavid du Colombier *p++ = 'f'; 7607dd7cddfSDavid du Colombier p = vlong2be(p, hz); 7617dd7cddfSDavid du Colombier if(write(bintimefd, b, p-b) < 0) 7627dd7cddfSDavid du Colombier sysfatal("writing /dev/bintime: %r"); 7637dd7cddfSDavid du Colombier } 7647dd7cddfSDavid du Colombier break; 7657dd7cddfSDavid du Colombier case Itiming: 7667dd7cddfSDavid du Colombier case Insec: 7677dd7cddfSDavid du Colombier seek(nsecfd, 0, 0); 7687dd7cddfSDavid du Colombier if(now >= 0 || delta != 0){ 7697dd7cddfSDavid du Colombier if(fprint(nsecfd, "%lld %lld %d", now, delta, n) < 0) 7707dd7cddfSDavid du Colombier sysfatal("writing /dev/nsec: %r"); 7717dd7cddfSDavid du Colombier } 7727dd7cddfSDavid du Colombier if(hz > 0){ 7737dd7cddfSDavid du Colombier seek(fastclockfd, 0, 0); 7747dd7cddfSDavid du Colombier if(fprint(fastclockfd, "%lld", hz) < 0) 7757dd7cddfSDavid du Colombier sysfatal("writing /dev/fastclock: %r"); 7767dd7cddfSDavid du Colombier } 7777dd7cddfSDavid du Colombier } 7787dd7cddfSDavid du Colombier } 7797dd7cddfSDavid du Colombier 7807dd7cddfSDavid du Colombier // 78180ee5cbfSDavid du Colombier // set priority high and wire process to a processor 7827dd7cddfSDavid du Colombier // 7837dd7cddfSDavid du Colombier static void 7847dd7cddfSDavid du Colombier setpriority(void) 7857dd7cddfSDavid du Colombier { 7867dd7cddfSDavid du Colombier int fd; 7877dd7cddfSDavid du Colombier char buf[32]; 7887dd7cddfSDavid du Colombier 7897dd7cddfSDavid du Colombier sprint(buf, "/proc/%d/ctl", getpid()); 7907dd7cddfSDavid du Colombier fd = open(buf, ORDWR); 7917dd7cddfSDavid du Colombier if(fd < 0){ 7927dd7cddfSDavid du Colombier fprint(2, "can't set priority\n"); 7937dd7cddfSDavid du Colombier return; 7947dd7cddfSDavid du Colombier } 7957dd7cddfSDavid du Colombier if(fprint(fd, "pri 100") < 0) 7967dd7cddfSDavid du Colombier fprint(2, "can't set priority\n"); 79780ee5cbfSDavid du Colombier if(fprint(fd, "wired 2") < 0) 79880ee5cbfSDavid du Colombier fprint(2, "can't wire process\n"); 7997dd7cddfSDavid du Colombier close(fd); 8007dd7cddfSDavid du Colombier } 80180ee5cbfSDavid du Colombier 80280ee5cbfSDavid du Colombier // convert to ntp timestamps 80380ee5cbfSDavid du Colombier static void 80480ee5cbfSDavid du Colombier hnputts(void *p, vlong nsec) 80580ee5cbfSDavid du Colombier { 80680ee5cbfSDavid du Colombier uchar *a; 80780ee5cbfSDavid du Colombier ulong tsh; 80880ee5cbfSDavid du Colombier ulong tsl; 80980ee5cbfSDavid du Colombier 81080ee5cbfSDavid du Colombier a = p; 81180ee5cbfSDavid du Colombier 81280ee5cbfSDavid du Colombier // zero is a special case 81380ee5cbfSDavid du Colombier if(nsec == 0) 81480ee5cbfSDavid du Colombier return; 81580ee5cbfSDavid du Colombier 81680ee5cbfSDavid du Colombier tsh = (nsec/SEC); 81780ee5cbfSDavid du Colombier nsec -= tsh*SEC; 81880ee5cbfSDavid du Colombier tsl = (nsec<<32)/SEC; 81980ee5cbfSDavid du Colombier hnputl(a, tsh+EPOCHDIFF); 82080ee5cbfSDavid du Colombier hnputl(a+4, tsl); 82180ee5cbfSDavid du Colombier } 82280ee5cbfSDavid du Colombier 82380ee5cbfSDavid du Colombier // convert from ntp timestamps 82480ee5cbfSDavid du Colombier static vlong 82580ee5cbfSDavid du Colombier nhgetts(void *p) 82680ee5cbfSDavid du Colombier { 82780ee5cbfSDavid du Colombier uchar *a; 82880ee5cbfSDavid du Colombier ulong tsh, tsl; 82980ee5cbfSDavid du Colombier vlong nsec; 83080ee5cbfSDavid du Colombier 83180ee5cbfSDavid du Colombier a = p; 83280ee5cbfSDavid du Colombier tsh = nhgetl(a); 83380ee5cbfSDavid du Colombier tsl = nhgetl(a+4); 83480ee5cbfSDavid du Colombier nsec = tsl*SEC; 83580ee5cbfSDavid du Colombier nsec >>= 32; 83680ee5cbfSDavid du Colombier nsec += (tsh - EPOCHDIFF)*SEC; 83780ee5cbfSDavid du Colombier return nsec; 83880ee5cbfSDavid du Colombier } 83980ee5cbfSDavid du Colombier 84080ee5cbfSDavid du Colombier // convert to ntp 32 bit fixed point 84180ee5cbfSDavid du Colombier static void 84280ee5cbfSDavid du Colombier hnputfp(void *p, vlong nsec) 84380ee5cbfSDavid du Colombier { 84480ee5cbfSDavid du Colombier uchar *a; 84580ee5cbfSDavid du Colombier ulong fp; 84680ee5cbfSDavid du Colombier 84780ee5cbfSDavid du Colombier a = p; 84880ee5cbfSDavid du Colombier 84980ee5cbfSDavid du Colombier fp = nsec/(SEC/((vlong)(1<<16))); 85080ee5cbfSDavid du Colombier hnputl(a, fp); 85180ee5cbfSDavid du Colombier } 85280ee5cbfSDavid du Colombier 85380ee5cbfSDavid du Colombier // convert from ntp fixed point to nanosecs 85480ee5cbfSDavid du Colombier static vlong 85580ee5cbfSDavid du Colombier nhgetfp(void *p) 85680ee5cbfSDavid du Colombier { 85780ee5cbfSDavid du Colombier uchar *a; 85880ee5cbfSDavid du Colombier ulong fp; 85980ee5cbfSDavid du Colombier vlong nsec; 86080ee5cbfSDavid du Colombier 86180ee5cbfSDavid du Colombier a = p; 86280ee5cbfSDavid du Colombier fp = nhgetl(a); 86380ee5cbfSDavid du Colombier nsec = ((vlong)fp)*(SEC/((vlong)(1<<16))); 86480ee5cbfSDavid du Colombier return nsec; 86580ee5cbfSDavid du Colombier } 86680ee5cbfSDavid du Colombier 86780ee5cbfSDavid du Colombier // get network address of the server 86880ee5cbfSDavid du Colombier static void 86980ee5cbfSDavid du Colombier setrootid(char *d) 87080ee5cbfSDavid du Colombier { 87180ee5cbfSDavid du Colombier char buf[128]; 87280ee5cbfSDavid du Colombier int fd, n; 87380ee5cbfSDavid du Colombier char *p; 87480ee5cbfSDavid du Colombier 87580ee5cbfSDavid du Colombier snprint(buf, sizeof(buf), "%s/remote", d); 87680ee5cbfSDavid du Colombier fd = open(buf, OREAD); 87780ee5cbfSDavid du Colombier if(fd < 0) 87880ee5cbfSDavid du Colombier return; 87980ee5cbfSDavid du Colombier n = read(fd, buf, sizeof buf); 88080ee5cbfSDavid du Colombier close(fd); 88180ee5cbfSDavid du Colombier if(n <= 0) 88280ee5cbfSDavid du Colombier return; 88380ee5cbfSDavid du Colombier p = strchr(buf, '!'); 88480ee5cbfSDavid du Colombier if(p != nil) 88580ee5cbfSDavid du Colombier *p = 0; 88680ee5cbfSDavid du Colombier v4parseip(rootid, buf); 88780ee5cbfSDavid du Colombier } 88880ee5cbfSDavid du Colombier 88980ee5cbfSDavid du Colombier static void 89080ee5cbfSDavid du Colombier ding(void*, char *s) 89180ee5cbfSDavid du Colombier { 89280ee5cbfSDavid du Colombier if(strstr(s, "alarm") != nil) 89380ee5cbfSDavid du Colombier noted(NCONT); 89480ee5cbfSDavid du Colombier noted(NDFLT); 89580ee5cbfSDavid du Colombier } 89680ee5cbfSDavid du Colombier 89780ee5cbfSDavid du Colombier static void 89880ee5cbfSDavid du Colombier addntpserver(char *name) 89980ee5cbfSDavid du Colombier { 90080ee5cbfSDavid du Colombier NTPserver *ns, **l; 90180ee5cbfSDavid du Colombier 90280ee5cbfSDavid du Colombier ns = mallocz(sizeof(NTPserver), 1); 90380ee5cbfSDavid du Colombier if(ns == nil) 90480ee5cbfSDavid du Colombier sysfatal("addntpserver: %r"); 9059a747e4fSDavid du Colombier timeserver = strdup(name); 90680ee5cbfSDavid du Colombier ns->name = name; 90780ee5cbfSDavid du Colombier for(l = &ntpservers; *l != nil; l = &(*l)->next) 90880ee5cbfSDavid du Colombier ; 90980ee5cbfSDavid du Colombier *l = ns; 91080ee5cbfSDavid du Colombier } 91180ee5cbfSDavid du Colombier 91280ee5cbfSDavid du Colombier // 91380ee5cbfSDavid du Colombier // sntp client, we keep calling if the delay seems 91480ee5cbfSDavid du Colombier // unusually high, i.e., 30% longer than avg. 91580ee5cbfSDavid du Colombier // 91680ee5cbfSDavid du Colombier static int 91780ee5cbfSDavid du Colombier ntptimediff(NTPserver *ns) 91880ee5cbfSDavid du Colombier { 91980ee5cbfSDavid du Colombier int fd, tries, n; 92080ee5cbfSDavid du Colombier NTPpkt ntpin, ntpout; 92180ee5cbfSDavid du Colombier vlong dt, recvts, origts, xmitts, destts, x; 92280ee5cbfSDavid du Colombier char dir[64]; 92380ee5cbfSDavid du Colombier 9249a747e4fSDavid du Colombier fd = dial(netmkaddr(ns->name, "udp", "ntp"), 0, dir, 0); 92580ee5cbfSDavid du Colombier if(fd < 0){ 92680ee5cbfSDavid du Colombier syslog(0, logfile, "can't reach %s: %r", ns->name); 92780ee5cbfSDavid du Colombier return -1; 92880ee5cbfSDavid du Colombier } 92980ee5cbfSDavid du Colombier setrootid(dir); 93080ee5cbfSDavid du Colombier notify(ding); 93180ee5cbfSDavid du Colombier 93280ee5cbfSDavid du Colombier memset(&ntpout, 0, sizeof(ntpout)); 93380ee5cbfSDavid du Colombier ntpout.mode = 3 | (3 << 3); 93480ee5cbfSDavid du Colombier 93580ee5cbfSDavid du Colombier for(tries = 0; tries < 3; tries++){ 93680ee5cbfSDavid du Colombier alarm(2*1000); 93780ee5cbfSDavid du Colombier 93880ee5cbfSDavid du Colombier gettime(&x, 0, 0); 93980ee5cbfSDavid du Colombier hnputts(ntpout.xmitts, x); 94080ee5cbfSDavid du Colombier if(write(fd, &ntpout, NTPSIZE) < 0){ 94180ee5cbfSDavid du Colombier alarm(0); 94280ee5cbfSDavid du Colombier continue; 94380ee5cbfSDavid du Colombier } 94480ee5cbfSDavid du Colombier 94580ee5cbfSDavid du Colombier n = read(fd, &ntpin, sizeof(ntpin)); 94680ee5cbfSDavid du Colombier alarm(0); 94780ee5cbfSDavid du Colombier gettime(&destts, 0, 0); 94880ee5cbfSDavid du Colombier if(n >= NTPSIZE){ 94980ee5cbfSDavid du Colombier close(fd); 95080ee5cbfSDavid du Colombier 95180ee5cbfSDavid du Colombier // we got one, use it 95280ee5cbfSDavid du Colombier recvts = nhgetts(ntpin.recvts); 95380ee5cbfSDavid du Colombier origts = nhgetts(ntpin.origts); 95480ee5cbfSDavid du Colombier xmitts = nhgetts(ntpin.xmitts); 95580ee5cbfSDavid du Colombier dt = ((recvts - origts) + (xmitts - destts))/2; 95680ee5cbfSDavid du Colombier 95780ee5cbfSDavid du Colombier // save results 95880ee5cbfSDavid du Colombier ns->rtt = ((destts - origts) - (xmitts - recvts))/2; 95980ee5cbfSDavid du Colombier ns->dt = dt; 96080ee5cbfSDavid du Colombier ns->stratum = ntpin.stratum; 96180ee5cbfSDavid du Colombier ns->precision = ntpin.precision; 96280ee5cbfSDavid du Colombier ns->rootdelay = nhgetfp(ntpin.rootdelay); 96380ee5cbfSDavid du Colombier ns->rootdisp = nhgetfp(ntpin.rootdisp); 96480ee5cbfSDavid du Colombier 96580ee5cbfSDavid du Colombier if(debug) 96680ee5cbfSDavid du Colombier fprint(2, "ntp %s stratum %d ntpdelay(%lld)\n", 96780ee5cbfSDavid du Colombier ns->name, ntpin.stratum, ns->rtt); 96880ee5cbfSDavid du Colombier return 0; 96980ee5cbfSDavid du Colombier } 97080ee5cbfSDavid du Colombier 97180ee5cbfSDavid du Colombier // try again 97280ee5cbfSDavid du Colombier sleep(250); 97380ee5cbfSDavid du Colombier } 97480ee5cbfSDavid du Colombier close(fd); 97580ee5cbfSDavid du Colombier return -1; 97680ee5cbfSDavid du Colombier } 97780ee5cbfSDavid du Colombier 97880ee5cbfSDavid du Colombier static vlong 97980ee5cbfSDavid du Colombier ntpsample(void) 98080ee5cbfSDavid du Colombier { 98180ee5cbfSDavid du Colombier NTPserver *tns, *ns; 98280ee5cbfSDavid du Colombier vlong metric, x; 98380ee5cbfSDavid du Colombier 98480ee5cbfSDavid du Colombier metric = 1000LL*SEC; 98580ee5cbfSDavid du Colombier ns = nil; 98680ee5cbfSDavid du Colombier for(tns = ntpservers; tns != nil; tns = tns->next){ 98780ee5cbfSDavid du Colombier if(ntptimediff(tns) < 0) 98880ee5cbfSDavid du Colombier continue; 98967031067SDavid du Colombier x = vabs(tns->rootdisp) + (vabs(tns->rtt+tns->rootdelay)>>1); 99095fdf19cSDavid du Colombier if(debug) 99195fdf19cSDavid du Colombier fprint(2, "ntp %s rootdelay %lld rootdisp %lld metric %lld\n", 99295fdf19cSDavid du Colombier tns->name, tns->rootdelay, tns->rootdisp, x); 99380ee5cbfSDavid du Colombier if(x < metric){ 99480ee5cbfSDavid du Colombier metric = x; 99580ee5cbfSDavid du Colombier ns = tns; 99680ee5cbfSDavid du Colombier } 99780ee5cbfSDavid du Colombier } 99880ee5cbfSDavid du Colombier 99980ee5cbfSDavid du Colombier if(ns == nil) 100080ee5cbfSDavid du Colombier return 0LL; 100180ee5cbfSDavid du Colombier 100280ee5cbfSDavid du Colombier // save data for our server 100380ee5cbfSDavid du Colombier rootdisp = ns->rootdisp; 100467031067SDavid du Colombier rootdelay = ns->rootdelay; 100580ee5cbfSDavid du Colombier mydelay = ns->rtt; 100667031067SDavid du Colombier mydisp = avgerr; 100780ee5cbfSDavid du Colombier if(ns->stratum == 0) 100880ee5cbfSDavid du Colombier stratum = 0; 100980ee5cbfSDavid du Colombier else 101080ee5cbfSDavid du Colombier stratum = ns->stratum + 1; 101180ee5cbfSDavid du Colombier 1012ed250ae1SDavid du Colombier ε = abs(ns->rtt/2); 101380ee5cbfSDavid du Colombier return ns->dt; 101480ee5cbfSDavid du Colombier } 101580ee5cbfSDavid du Colombier 101680ee5cbfSDavid du Colombier // 10175fab9909SDavid du Colombier // sample the utc file 10185fab9909SDavid du Colombier // 10195fab9909SDavid du Colombier static vlong 10205fab9909SDavid du Colombier utcsample(void) 10215fab9909SDavid du Colombier { 10225fab9909SDavid du Colombier vlong s; 10235fab9909SDavid du Colombier int n; 10245fab9909SDavid du Colombier char *v[2], buf[128]; 10255fab9909SDavid du Colombier 10265fab9909SDavid du Colombier s = 0; 10275fab9909SDavid du Colombier seek(utcfil, 0, 0); 10285fab9909SDavid du Colombier n = read(utcfil, buf, sizeof buf - 1); 10295fab9909SDavid du Colombier if (n <= 0) 10305fab9909SDavid du Colombier return(0LL); 10315fab9909SDavid du Colombier buf[n] = 0; 10325fab9909SDavid du Colombier n = tokenize(buf, v, nelem(v)); 10335fab9909SDavid du Colombier if (strcmp(v[0], "0") == 0) 10345fab9909SDavid du Colombier return(0LL); 10355fab9909SDavid du Colombier if (n == 2) { 10365fab9909SDavid du Colombier gettime(&s, nil, nil); 10375fab9909SDavid du Colombier s -= atoll(v[1]); 10385fab9909SDavid du Colombier } 10391118d624SDavid du Colombier lastutc = atoll(v[0]) + s; 10401118d624SDavid du Colombier return(lastutc); 10415fab9909SDavid du Colombier } 10425fab9909SDavid du Colombier 10435fab9909SDavid du Colombier // 104480ee5cbfSDavid du Colombier // sntp server 104580ee5cbfSDavid du Colombier // 104680ee5cbfSDavid du Colombier static int 104780ee5cbfSDavid du Colombier openlisten(char *net) 104880ee5cbfSDavid du Colombier { 104980ee5cbfSDavid du Colombier int fd, cfd; 105080ee5cbfSDavid du Colombier char data[128]; 105180ee5cbfSDavid du Colombier char devdir[40]; 105280ee5cbfSDavid du Colombier 105380ee5cbfSDavid du Colombier sprint(data, "%s/udp!*!ntp", net); 105480ee5cbfSDavid du Colombier cfd = announce(data, devdir); 105580ee5cbfSDavid du Colombier if(cfd < 0) 105680ee5cbfSDavid du Colombier sysfatal("can't announce"); 105780ee5cbfSDavid du Colombier if(fprint(cfd, "headers") < 0) 105880ee5cbfSDavid du Colombier sysfatal("can't set header mode"); 1059d9dc5dd1SDavid du Colombier fprint(cfd, "oldheaders"); 106080ee5cbfSDavid du Colombier 106180ee5cbfSDavid du Colombier sprint(data, "%s/data", devdir); 106280ee5cbfSDavid du Colombier 106380ee5cbfSDavid du Colombier fd = open(data, ORDWR); 106480ee5cbfSDavid du Colombier if(fd < 0) 106580ee5cbfSDavid du Colombier sysfatal("open udp data"); 106680ee5cbfSDavid du Colombier return fd; 106780ee5cbfSDavid du Colombier } 106880ee5cbfSDavid du Colombier static void 10699a747e4fSDavid du Colombier ntpserver(char *servenet) 107080ee5cbfSDavid du Colombier { 107180ee5cbfSDavid du Colombier int fd, n; 107280ee5cbfSDavid du Colombier NTPpkt *ntp; 107380ee5cbfSDavid du Colombier char buf[512]; 107480ee5cbfSDavid du Colombier int vers, mode; 107580ee5cbfSDavid du Colombier vlong recvts, x; 107680ee5cbfSDavid du Colombier 10779a747e4fSDavid du Colombier fd = openlisten(servenet); 107880ee5cbfSDavid du Colombier 10795fab9909SDavid du Colombier if (Rootid == nil) 108080ee5cbfSDavid du Colombier switch(type){ 108180ee5cbfSDavid du Colombier case Fs: 10825fab9909SDavid du Colombier Rootid = "WWV"; 108380ee5cbfSDavid du Colombier break; 108480ee5cbfSDavid du Colombier case Rtc: 10855fab9909SDavid du Colombier Rootid = "LOCL"; 10865fab9909SDavid du Colombier break; 10875fab9909SDavid du Colombier case Utc: 10885fab9909SDavid du Colombier Rootid = "UTC"; 108980ee5cbfSDavid du Colombier break; 109080ee5cbfSDavid du Colombier case Ntp: 109180ee5cbfSDavid du Colombier /* set by the ntp client */ 109280ee5cbfSDavid du Colombier break; 109380ee5cbfSDavid du Colombier } 10945fab9909SDavid du Colombier if (Rootid != nil) 10955fab9909SDavid du Colombier memmove(rootid, Rootid, strlen(Rootid) > 4 ? 4 : strlen(Rootid)); 109680ee5cbfSDavid du Colombier 109780ee5cbfSDavid du Colombier for(;;){ 109880ee5cbfSDavid du Colombier n = read(fd, buf, sizeof(buf)); 109980ee5cbfSDavid du Colombier gettime(&recvts, 0, 0); 110080ee5cbfSDavid du Colombier if(n < 0) 110180ee5cbfSDavid du Colombier return; 1102d9dc5dd1SDavid du Colombier if(n < OUdphdrsize + NTPSIZE) 110380ee5cbfSDavid du Colombier continue; 110480ee5cbfSDavid du Colombier 1105d9dc5dd1SDavid du Colombier ntp = (NTPpkt*)(buf+OUdphdrsize); 110680ee5cbfSDavid du Colombier mode = ntp->mode & 7; 110780ee5cbfSDavid du Colombier vers = (ntp->mode>>3) & 7; 110880ee5cbfSDavid du Colombier if(mode != 3) 110980ee5cbfSDavid du Colombier continue; 111080ee5cbfSDavid du Colombier 111180ee5cbfSDavid du Colombier ntp->mode = (vers<<3)|4; 111280ee5cbfSDavid du Colombier ntp->stratum = stratum; 111367031067SDavid du Colombier ntp->precision = myprec; 111480ee5cbfSDavid du Colombier hnputfp(ntp->rootdelay, rootdelay + mydelay); 111580ee5cbfSDavid du Colombier hnputfp(ntp->rootdisp, rootdisp + mydisp); 11161118d624SDavid du Colombier hnputts(ntp->refts, lastutc); 111780ee5cbfSDavid du Colombier memmove(ntp->origts, ntp->xmitts, sizeof(ntp->origts)); 111880ee5cbfSDavid du Colombier hnputts(ntp->recvts, recvts); 111980ee5cbfSDavid du Colombier memmove(ntp->rootid, rootid, sizeof(ntp->rootid)); 112080ee5cbfSDavid du Colombier gettime(&x, 0, 0); 112180ee5cbfSDavid du Colombier hnputts(ntp->xmitts, x); 1122d9dc5dd1SDavid du Colombier write(fd, buf, NTPSIZE+OUdphdrsize); 112380ee5cbfSDavid du Colombier } 112480ee5cbfSDavid du Colombier } 112580ee5cbfSDavid du Colombier 112680ee5cbfSDavid du Colombier // 112780ee5cbfSDavid du Colombier // get the current time from the file system 112880ee5cbfSDavid du Colombier // 112980ee5cbfSDavid du Colombier static long 113080ee5cbfSDavid du Colombier fstime(void) 113180ee5cbfSDavid du Colombier { 11329a747e4fSDavid du Colombier Dir *d; 11339a747e4fSDavid du Colombier ulong t; 113480ee5cbfSDavid du Colombier 11359a747e4fSDavid du Colombier d = dirstat("/n/boot"); 11369a747e4fSDavid du Colombier if(d != nil){ 11379a747e4fSDavid du Colombier t = d->atime; 11389a747e4fSDavid du Colombier free(d); 11399a747e4fSDavid du Colombier } else 11409a747e4fSDavid du Colombier t = 0; 11419a747e4fSDavid du Colombier return t; 114280ee5cbfSDavid du Colombier } 114380ee5cbfSDavid du Colombier 114480ee5cbfSDavid du Colombier // 114580ee5cbfSDavid du Colombier // get the current time from the real time clock 114680ee5cbfSDavid du Colombier // 114780ee5cbfSDavid du Colombier static long 114880ee5cbfSDavid du Colombier rtctime(void) 114980ee5cbfSDavid du Colombier { 115080ee5cbfSDavid du Colombier char b[20]; 115180ee5cbfSDavid du Colombier static int f = -1; 115280ee5cbfSDavid du Colombier int i, retries; 115380ee5cbfSDavid du Colombier 115480ee5cbfSDavid du Colombier memset(b, 0, sizeof(b)); 115580ee5cbfSDavid du Colombier for(retries = 0; retries < 100; retries++){ 115680ee5cbfSDavid du Colombier if(f < 0) 115780ee5cbfSDavid du Colombier f = open("/dev/rtc", OREAD|OCEXEC); 115880ee5cbfSDavid du Colombier if(f < 0) 115980ee5cbfSDavid du Colombier break; 116080ee5cbfSDavid du Colombier if(seek(f, 0, 0) < 0 || (i = read(f, b, sizeof(b))) < 0){ 116180ee5cbfSDavid du Colombier close(f); 116280ee5cbfSDavid du Colombier f = -1; 116380ee5cbfSDavid du Colombier } else { 116480ee5cbfSDavid du Colombier if(i != 0) 116580ee5cbfSDavid du Colombier break; 116680ee5cbfSDavid du Colombier } 116780ee5cbfSDavid du Colombier } 116880ee5cbfSDavid du Colombier return strtoul(b, 0, 10)+gmtdelta; 116980ee5cbfSDavid du Colombier } 117080ee5cbfSDavid du Colombier 117180ee5cbfSDavid du Colombier 117280ee5cbfSDavid du Colombier // 117380ee5cbfSDavid du Colombier // Sample a clock. We wait for the clock to always 117480ee5cbfSDavid du Colombier // be at the leading edge of a clock period. 117580ee5cbfSDavid du Colombier // 117680ee5cbfSDavid du Colombier static vlong 117780ee5cbfSDavid du Colombier sample(long (*get)(void)) 117880ee5cbfSDavid du Colombier { 117980ee5cbfSDavid du Colombier long this, last; 118080ee5cbfSDavid du Colombier vlong start, end; 118180ee5cbfSDavid du Colombier 118280ee5cbfSDavid du Colombier /* 118380ee5cbfSDavid du Colombier * wait for the second to change 118480ee5cbfSDavid du Colombier */ 118580ee5cbfSDavid du Colombier last = (*get)(); 118680ee5cbfSDavid du Colombier for(;;){ 118780ee5cbfSDavid du Colombier gettime(&start, 0, 0); 118880ee5cbfSDavid du Colombier this = (*get)(); 118980ee5cbfSDavid du Colombier gettime(&end, 0, 0); 119080ee5cbfSDavid du Colombier if(this != last) 119180ee5cbfSDavid du Colombier break; 119280ee5cbfSDavid du Colombier last = this; 119380ee5cbfSDavid du Colombier } 119480ee5cbfSDavid du Colombier return SEC*this - (end-start)/2; 119580ee5cbfSDavid du Colombier } 119680ee5cbfSDavid du Colombier 119780ee5cbfSDavid du Colombier // 119880ee5cbfSDavid du Colombier // the name of the frequency file has the method and possibly the 119980ee5cbfSDavid du Colombier // server name encoded in it. 120080ee5cbfSDavid du Colombier // 120180ee5cbfSDavid du Colombier static int 120280ee5cbfSDavid du Colombier openfreqfile(void) 120380ee5cbfSDavid du Colombier { 12049a747e4fSDavid du Colombier char buf[29]; 120580ee5cbfSDavid du Colombier int fd; 120680ee5cbfSDavid du Colombier 120780ee5cbfSDavid du Colombier if(sysid == nil) 120880ee5cbfSDavid du Colombier return -1; 120980ee5cbfSDavid du Colombier 121080ee5cbfSDavid du Colombier switch(type){ 121180ee5cbfSDavid du Colombier case Ntp: 121280ee5cbfSDavid du Colombier snprint(buf, sizeof buf, "%s/ts.%s.%d.%s", dir, sysid, type, timeserver); 121380ee5cbfSDavid du Colombier break; 121480ee5cbfSDavid du Colombier default: 121580ee5cbfSDavid du Colombier snprint(buf, sizeof buf, "%s/ts.%s.%d", dir, sysid, type); 121680ee5cbfSDavid du Colombier break; 121780ee5cbfSDavid du Colombier } 121880ee5cbfSDavid du Colombier fd = open(buf, ORDWR); 121980ee5cbfSDavid du Colombier if(fd < 0) 122080ee5cbfSDavid du Colombier fd = create(buf, ORDWR, 0666); 122180ee5cbfSDavid du Colombier if(fd < 0) 122280ee5cbfSDavid du Colombier return -1; 122380ee5cbfSDavid du Colombier return fd; 122480ee5cbfSDavid du Colombier } 122580ee5cbfSDavid du Colombier 122680ee5cbfSDavid du Colombier // 122780ee5cbfSDavid du Colombier // the file contains the last known frequency and the 122880ee5cbfSDavid du Colombier // number of seconds it was sampled over 122980ee5cbfSDavid du Colombier // 123080ee5cbfSDavid du Colombier static vlong 123180ee5cbfSDavid du Colombier readfreqfile(int fd, vlong ohz, vlong minhz, vlong maxhz) 123280ee5cbfSDavid du Colombier { 123380ee5cbfSDavid du Colombier int n; 123480ee5cbfSDavid du Colombier char buf[128]; 123580ee5cbfSDavid du Colombier vlong hz; 123680ee5cbfSDavid du Colombier 123780ee5cbfSDavid du Colombier n = read(fd, buf, sizeof(buf)-1); 123880ee5cbfSDavid du Colombier if(n <= 0) 123980ee5cbfSDavid du Colombier return ohz; 124080ee5cbfSDavid du Colombier buf[n] = 0; 124180ee5cbfSDavid du Colombier hz = strtoll(buf, nil, 0); 124280ee5cbfSDavid du Colombier 124380ee5cbfSDavid du Colombier if(hz > maxhz || hz < minhz) 124480ee5cbfSDavid du Colombier return ohz; 124580ee5cbfSDavid du Colombier 124680ee5cbfSDavid du Colombier settime(-1, hz, 0, 0); 124780ee5cbfSDavid du Colombier return hz; 124880ee5cbfSDavid du Colombier } 124980ee5cbfSDavid du Colombier 125080ee5cbfSDavid du Colombier // 125180ee5cbfSDavid du Colombier // remember hz and averaging period 125280ee5cbfSDavid du Colombier // 125380ee5cbfSDavid du Colombier static void 125480ee5cbfSDavid du Colombier writefreqfile(int fd, vlong hz, int secs, vlong diff) 125580ee5cbfSDavid du Colombier { 125680ee5cbfSDavid du Colombier long now; 125780ee5cbfSDavid du Colombier static long last; 125880ee5cbfSDavid du Colombier 125980ee5cbfSDavid du Colombier if(fd < 0) 126080ee5cbfSDavid du Colombier return; 126180ee5cbfSDavid du Colombier now = time(0); 12629a747e4fSDavid du Colombier if(now - last < 10*60) 126380ee5cbfSDavid du Colombier return; 126480ee5cbfSDavid du Colombier last = now; 126580ee5cbfSDavid du Colombier if(seek(fd, 0, 0) < 0) 126680ee5cbfSDavid du Colombier return; 126780ee5cbfSDavid du Colombier fprint(fd, "%lld %d %d %lld\n", hz, secs, type, diff); 126880ee5cbfSDavid du Colombier } 126980ee5cbfSDavid du Colombier 127080ee5cbfSDavid du Colombier static uvlong 127180ee5cbfSDavid du Colombier vabs(vlong x) 127280ee5cbfSDavid du Colombier { 127380ee5cbfSDavid du Colombier if(x < 0LL) 127480ee5cbfSDavid du Colombier return (uvlong)-x; 127580ee5cbfSDavid du Colombier else 127680ee5cbfSDavid du Colombier return (uvlong)x; 127780ee5cbfSDavid du Colombier } 127895fdf19cSDavid du Colombier 127995fdf19cSDavid du Colombier static void 128095fdf19cSDavid du Colombier background(void) 128195fdf19cSDavid du Colombier { 128295fdf19cSDavid du Colombier static int inbackground; 128395fdf19cSDavid du Colombier 128495fdf19cSDavid du Colombier if(inbackground) 128595fdf19cSDavid du Colombier return; 128695fdf19cSDavid du Colombier 128795fdf19cSDavid du Colombier if(!debug) { 128895fdf19cSDavid du Colombier switch(rfork(RFPROC|RFFDG|RFNAMEG|RFNOTEG|RFNOWAIT)){ 128995fdf19cSDavid du Colombier case -1: 129095fdf19cSDavid du Colombier sysfatal("forking: %r"); 129195fdf19cSDavid du Colombier break; 129295fdf19cSDavid du Colombier case 0: 129395fdf19cSDavid du Colombier break; 129495fdf19cSDavid du Colombier default: 129595fdf19cSDavid du Colombier exits(0); 129695fdf19cSDavid du Colombier } 129795fdf19cSDavid du Colombier } 129895fdf19cSDavid du Colombier inbackground = 1; 129995fdf19cSDavid du Colombier } 130067031067SDavid du Colombier 130167031067SDavid du Colombier static int 130267031067SDavid du Colombier getclockprecision(vlong hz) 130367031067SDavid du Colombier { 130467031067SDavid du Colombier int i; 130567031067SDavid du Colombier 130667031067SDavid du Colombier i = 8; 130767031067SDavid du Colombier while(hz > 0){ 130867031067SDavid du Colombier i--; 130967031067SDavid du Colombier hz >>= 1; 131067031067SDavid du Colombier } 131167031067SDavid du Colombier return i; 131267031067SDavid du Colombier } 1313