19ef1f84bSDavid du Colombier #include "u.h"
29ef1f84bSDavid du Colombier #include "../port/lib.h"
39ef1f84bSDavid du Colombier #include "mem.h"
49ef1f84bSDavid du Colombier #include "dat.h"
59ef1f84bSDavid du Colombier #include "fns.h"
69ef1f84bSDavid du Colombier
79ef1f84bSDavid du Colombier #include "ureg.h"
8*406c76faSDavid du Colombier #include "../port/error.h"
9*406c76faSDavid du Colombier
10*406c76faSDavid du Colombier enum {
11*406c76faSDavid du Colombier Maxtimerloops = 20*1000,
12*406c76faSDavid du Colombier };
139ef1f84bSDavid du Colombier
149ef1f84bSDavid du Colombier struct Timers
159ef1f84bSDavid du Colombier {
169ef1f84bSDavid du Colombier Lock;
179ef1f84bSDavid du Colombier Timer *head;
189ef1f84bSDavid du Colombier };
199ef1f84bSDavid du Colombier
209ef1f84bSDavid du Colombier static Timers timers[MACHMAX];
21*406c76faSDavid du Colombier static int timersinited;
229ef1f84bSDavid du Colombier
239ef1f84bSDavid du Colombier ulong intrcount[MACHMAX];
249ef1f84bSDavid du Colombier ulong fcallcount[MACHMAX];
259ef1f84bSDavid du Colombier
269ef1f84bSDavid du Colombier static vlong
tadd(Timers * tt,Timer * nt)279ef1f84bSDavid du Colombier tadd(Timers *tt, Timer *nt)
289ef1f84bSDavid du Colombier {
299ef1f84bSDavid du Colombier vlong when;
309ef1f84bSDavid du Colombier Timer *t, **last;
319ef1f84bSDavid du Colombier
329ef1f84bSDavid du Colombier /* Called with tt locked */
339ef1f84bSDavid du Colombier assert(nt->tt == nil);
349ef1f84bSDavid du Colombier switch(nt->tmode){
359ef1f84bSDavid du Colombier default:
369ef1f84bSDavid du Colombier panic("timer");
379ef1f84bSDavid du Colombier break;
389ef1f84bSDavid du Colombier case Trelative:
399ef1f84bSDavid du Colombier if(nt->tns <= 0)
409ef1f84bSDavid du Colombier nt->tns = 1;
419ef1f84bSDavid du Colombier nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
429ef1f84bSDavid du Colombier break;
439ef1f84bSDavid du Colombier case Tperiodic:
449ef1f84bSDavid du Colombier /*
459ef1f84bSDavid du Colombier * Periodic timers must have a period of at least 100µs.
469ef1f84bSDavid du Colombier */
479ef1f84bSDavid du Colombier assert(nt->tns >= 100000);
489ef1f84bSDavid du Colombier if(nt->twhen == 0){
499ef1f84bSDavid du Colombier /*
509ef1f84bSDavid du Colombier * Look for another timer at the
519ef1f84bSDavid du Colombier * same frequency for combining.
529ef1f84bSDavid du Colombier */
539ef1f84bSDavid du Colombier for(t = tt->head; t; t = t->tnext){
549ef1f84bSDavid du Colombier if(t->tmode == Tperiodic && t->tns == nt->tns)
559ef1f84bSDavid du Colombier break;
569ef1f84bSDavid du Colombier }
579ef1f84bSDavid du Colombier if(t)
589ef1f84bSDavid du Colombier nt->twhen = t->twhen;
599ef1f84bSDavid du Colombier else
609ef1f84bSDavid du Colombier nt->twhen = fastticks(nil);
619ef1f84bSDavid du Colombier }
629ef1f84bSDavid du Colombier
639ef1f84bSDavid du Colombier /*
649ef1f84bSDavid du Colombier * The new time must be in the future.
659ef1f84bSDavid du Colombier * ns2fastticks() can return 0 if the tod clock
669ef1f84bSDavid du Colombier * has been adjusted by, e.g. timesync.
679ef1f84bSDavid du Colombier */
689ef1f84bSDavid du Colombier when = ns2fastticks(nt->tns);
699ef1f84bSDavid du Colombier if(when == 0)
709ef1f84bSDavid du Colombier when = 1;
719ef1f84bSDavid du Colombier nt->twhen += when;
729ef1f84bSDavid du Colombier break;
739ef1f84bSDavid du Colombier }
749ef1f84bSDavid du Colombier
759ef1f84bSDavid du Colombier for(last = &tt->head; t = *last; last = &t->tnext){
769ef1f84bSDavid du Colombier if(t->twhen > nt->twhen)
779ef1f84bSDavid du Colombier break;
789ef1f84bSDavid du Colombier }
799ef1f84bSDavid du Colombier nt->tnext = *last;
809ef1f84bSDavid du Colombier *last = nt;
819ef1f84bSDavid du Colombier nt->tt = tt;
829ef1f84bSDavid du Colombier if(last == &tt->head)
839ef1f84bSDavid du Colombier return nt->twhen;
849ef1f84bSDavid du Colombier return 0;
859ef1f84bSDavid du Colombier }
869ef1f84bSDavid du Colombier
879ef1f84bSDavid du Colombier static vlong
tdel(Timer * dt)889ef1f84bSDavid du Colombier tdel(Timer *dt)
899ef1f84bSDavid du Colombier {
909ef1f84bSDavid du Colombier Timer *t, **last;
919ef1f84bSDavid du Colombier Timers *tt;
929ef1f84bSDavid du Colombier
939ef1f84bSDavid du Colombier tt = dt->tt;
949ef1f84bSDavid du Colombier if(tt == nil)
959ef1f84bSDavid du Colombier return 0;
969ef1f84bSDavid du Colombier for(last = &tt->head; t = *last; last = &t->tnext){
979ef1f84bSDavid du Colombier if(t == dt){
989ef1f84bSDavid du Colombier assert(dt->tt);
999ef1f84bSDavid du Colombier dt->tt = nil;
1009ef1f84bSDavid du Colombier *last = t->tnext;
1019ef1f84bSDavid du Colombier break;
1029ef1f84bSDavid du Colombier }
1039ef1f84bSDavid du Colombier }
1049ef1f84bSDavid du Colombier if(last == &tt->head && tt->head)
1059ef1f84bSDavid du Colombier return tt->head->twhen;
1069ef1f84bSDavid du Colombier return 0;
1079ef1f84bSDavid du Colombier }
1089ef1f84bSDavid du Colombier
1099ef1f84bSDavid du Colombier /* add or modify a timer */
1109ef1f84bSDavid du Colombier void
timeradd(Timer * nt)1119ef1f84bSDavid du Colombier timeradd(Timer *nt)
1129ef1f84bSDavid du Colombier {
1139ef1f84bSDavid du Colombier Timers *tt;
1149ef1f84bSDavid du Colombier vlong when;
1159ef1f84bSDavid du Colombier
1169ef1f84bSDavid du Colombier /* Must lock Timer struct before Timers struct */
1179ef1f84bSDavid du Colombier ilock(nt);
1189ef1f84bSDavid du Colombier if(tt = nt->tt){
1199ef1f84bSDavid du Colombier ilock(tt);
1209ef1f84bSDavid du Colombier tdel(nt);
1219ef1f84bSDavid du Colombier iunlock(tt);
1229ef1f84bSDavid du Colombier }
1239ef1f84bSDavid du Colombier tt = &timers[m->machno];
1249ef1f84bSDavid du Colombier ilock(tt);
1259ef1f84bSDavid du Colombier when = tadd(tt, nt);
1269ef1f84bSDavid du Colombier if(when)
1279ef1f84bSDavid du Colombier timerset(when);
1289ef1f84bSDavid du Colombier iunlock(tt);
1299ef1f84bSDavid du Colombier iunlock(nt);
1309ef1f84bSDavid du Colombier }
1319ef1f84bSDavid du Colombier
1329ef1f84bSDavid du Colombier
1339ef1f84bSDavid du Colombier void
timerdel(Timer * dt)1349ef1f84bSDavid du Colombier timerdel(Timer *dt)
1359ef1f84bSDavid du Colombier {
1369ef1f84bSDavid du Colombier Timers *tt;
1379ef1f84bSDavid du Colombier vlong when;
1389ef1f84bSDavid du Colombier
1399ef1f84bSDavid du Colombier ilock(dt);
1409ef1f84bSDavid du Colombier if(tt = dt->tt){
1419ef1f84bSDavid du Colombier ilock(tt);
1429ef1f84bSDavid du Colombier when = tdel(dt);
1439ef1f84bSDavid du Colombier if(when && tt == &timers[m->machno])
1449ef1f84bSDavid du Colombier timerset(tt->head->twhen);
1459ef1f84bSDavid du Colombier iunlock(tt);
1469ef1f84bSDavid du Colombier }
1479ef1f84bSDavid du Colombier iunlock(dt);
1489ef1f84bSDavid du Colombier }
1499ef1f84bSDavid du Colombier
1509ef1f84bSDavid du Colombier void
hzclock(Ureg * ur)1519ef1f84bSDavid du Colombier hzclock(Ureg *ur)
1529ef1f84bSDavid du Colombier {
1539ef1f84bSDavid du Colombier uintptr pc;
1549ef1f84bSDavid du Colombier
1559ef1f84bSDavid du Colombier m->ticks++;
1569ef1f84bSDavid du Colombier if(m->machno == 0)
1579ef1f84bSDavid du Colombier sys->ticks = m->ticks;
1589ef1f84bSDavid du Colombier
1599ef1f84bSDavid du Colombier pc = userpc(ur);
1609ef1f84bSDavid du Colombier if(m->proc)
1619ef1f84bSDavid du Colombier m->proc->pc = pc;
1629ef1f84bSDavid du Colombier
1639ef1f84bSDavid du Colombier if(m->mmuflush){
1649ef1f84bSDavid du Colombier if(up)
1659ef1f84bSDavid du Colombier mmuflush();
1669ef1f84bSDavid du Colombier m->mmuflush = 0;
1679ef1f84bSDavid du Colombier }
1689ef1f84bSDavid du Colombier
1699ef1f84bSDavid du Colombier accounttime();
1709ef1f84bSDavid du Colombier kmapinval();
1719ef1f84bSDavid du Colombier
1729ef1f84bSDavid du Colombier if(kproftimer != nil)
1739ef1f84bSDavid du Colombier kproftimer(pc);
1749ef1f84bSDavid du Colombier
1759ef1f84bSDavid du Colombier if((active.machs&(1<<m->machno)) == 0)
1769ef1f84bSDavid du Colombier return;
1779ef1f84bSDavid du Colombier
1789ef1f84bSDavid du Colombier if(active.exiting) {
1799ef1f84bSDavid du Colombier iprint("someone's exiting\n");
1809ef1f84bSDavid du Colombier exit(0);
1819ef1f84bSDavid du Colombier }
1829ef1f84bSDavid du Colombier
1839ef1f84bSDavid du Colombier checkalarms();
1849ef1f84bSDavid du Colombier
1859ef1f84bSDavid du Colombier if(up && up->state == Running)
1869ef1f84bSDavid du Colombier hzsched(); /* in proc.c */
1879ef1f84bSDavid du Colombier }
1889ef1f84bSDavid du Colombier
1899ef1f84bSDavid du Colombier void
timerintr(Ureg * u,Tval)190*406c76faSDavid du Colombier timerintr(Ureg *u, Tval)
1919ef1f84bSDavid du Colombier {
1929ef1f84bSDavid du Colombier Timer *t;
1939ef1f84bSDavid du Colombier Timers *tt;
1949ef1f84bSDavid du Colombier vlong when, now;
195*406c76faSDavid du Colombier int count, callhzclock;
1969ef1f84bSDavid du Colombier
1979ef1f84bSDavid du Colombier intrcount[m->machno]++;
1989ef1f84bSDavid du Colombier callhzclock = 0;
1999ef1f84bSDavid du Colombier tt = &timers[m->machno];
2009ef1f84bSDavid du Colombier now = fastticks(nil);
201*406c76faSDavid du Colombier if(now == 0)
202*406c76faSDavid du Colombier panic("timerintr: zero fastticks()");
2039ef1f84bSDavid du Colombier ilock(tt);
204*406c76faSDavid du Colombier count = Maxtimerloops;
205*406c76faSDavid du Colombier while((t = tt->head) != nil){
2069ef1f84bSDavid du Colombier /*
2079ef1f84bSDavid du Colombier * No need to ilock t here: any manipulation of t
2089ef1f84bSDavid du Colombier * requires tdel(t) and this must be done with a
2099ef1f84bSDavid du Colombier * lock to tt held. We have tt, so the tdel will
2109ef1f84bSDavid du Colombier * wait until we're done
2119ef1f84bSDavid du Colombier */
2129ef1f84bSDavid du Colombier when = t->twhen;
2139ef1f84bSDavid du Colombier if(when > now){
2149ef1f84bSDavid du Colombier timerset(when);
2159ef1f84bSDavid du Colombier iunlock(tt);
2169ef1f84bSDavid du Colombier if(callhzclock)
2179ef1f84bSDavid du Colombier hzclock(u);
2189ef1f84bSDavid du Colombier return;
2199ef1f84bSDavid du Colombier }
2209ef1f84bSDavid du Colombier tt->head = t->tnext;
2219ef1f84bSDavid du Colombier assert(t->tt == tt);
2229ef1f84bSDavid du Colombier t->tt = nil;
2239ef1f84bSDavid du Colombier fcallcount[m->machno]++;
2249ef1f84bSDavid du Colombier iunlock(tt);
2259ef1f84bSDavid du Colombier if(t->tf)
2269ef1f84bSDavid du Colombier (*t->tf)(u, t);
2279ef1f84bSDavid du Colombier else
2289ef1f84bSDavid du Colombier callhzclock++;
2299ef1f84bSDavid du Colombier ilock(tt);
2309ef1f84bSDavid du Colombier if(t->tmode == Tperiodic)
2319ef1f84bSDavid du Colombier tadd(tt, t);
232*406c76faSDavid du Colombier if (--count <= 0) {
233*406c76faSDavid du Colombier count = Maxtimerloops;
234*406c76faSDavid du Colombier iprint("timerintr: probably stuck in while loop; "
235*406c76faSDavid du Colombier "scrutinise clock.c or use faster cycle "
236*406c76faSDavid du Colombier "counter\n");
237*406c76faSDavid du Colombier }
2389ef1f84bSDavid du Colombier }
2399ef1f84bSDavid du Colombier iunlock(tt);
2409ef1f84bSDavid du Colombier }
2419ef1f84bSDavid du Colombier
2429ef1f84bSDavid du Colombier void
timersinit(void)2439ef1f84bSDavid du Colombier timersinit(void)
2449ef1f84bSDavid du Colombier {
2459ef1f84bSDavid du Colombier Timer *t;
2469ef1f84bSDavid du Colombier
2479ef1f84bSDavid du Colombier /*
2489ef1f84bSDavid du Colombier * T->tf == nil means the HZ clock for this processor.
2499ef1f84bSDavid du Colombier */
250*406c76faSDavid du Colombier timersinited = 1;
2519ef1f84bSDavid du Colombier todinit();
2529ef1f84bSDavid du Colombier t = malloc(sizeof(*t));
253*406c76faSDavid du Colombier if(t == nil)
254*406c76faSDavid du Colombier error(Enomem);
2559ef1f84bSDavid du Colombier t->tmode = Tperiodic;
2569ef1f84bSDavid du Colombier t->tt = nil;
2579ef1f84bSDavid du Colombier t->tns = 1000000000/HZ;
2589ef1f84bSDavid du Colombier t->tf = nil;
2599ef1f84bSDavid du Colombier timeradd(t);
2609ef1f84bSDavid du Colombier }
2619ef1f84bSDavid du Colombier
2629ef1f84bSDavid du Colombier Timer*
addclock0link(void (* f)(void),int ms)2639ef1f84bSDavid du Colombier addclock0link(void (*f)(void), int ms)
2649ef1f84bSDavid du Colombier {
2659ef1f84bSDavid du Colombier Timer *nt;
2669ef1f84bSDavid du Colombier vlong when;
2679ef1f84bSDavid du Colombier
268*406c76faSDavid du Colombier if(!timersinited)
269*406c76faSDavid du Colombier panic("addclock0link: timersinit not called yet");
2709ef1f84bSDavid du Colombier /* Synchronize to hztimer if ms is 0 */
2719ef1f84bSDavid du Colombier nt = malloc(sizeof(Timer));
272*406c76faSDavid du Colombier if(nt == nil)
273*406c76faSDavid du Colombier error(Enomem);
2749ef1f84bSDavid du Colombier if(ms == 0)
2759ef1f84bSDavid du Colombier ms = 1000/HZ;
2769ef1f84bSDavid du Colombier nt->tns = (vlong)ms*1000000LL;
2779ef1f84bSDavid du Colombier nt->tmode = Tperiodic;
2789ef1f84bSDavid du Colombier nt->tt = nil;
2799ef1f84bSDavid du Colombier nt->tf = (void (*)(Ureg*, Timer*))f;
2809ef1f84bSDavid du Colombier
2819ef1f84bSDavid du Colombier ilock(&timers[0]);
2829ef1f84bSDavid du Colombier when = tadd(&timers[0], nt);
2839ef1f84bSDavid du Colombier if(when)
2849ef1f84bSDavid du Colombier timerset(when);
2859ef1f84bSDavid du Colombier iunlock(&timers[0]);
2869ef1f84bSDavid du Colombier return nt;
2879ef1f84bSDavid du Colombier }
2889ef1f84bSDavid du Colombier
2899ef1f84bSDavid du Colombier /*
2909ef1f84bSDavid du Colombier * This tk2ms avoids overflows that the macro version is prone to.
2919ef1f84bSDavid du Colombier * It is a LOT slower so shouldn't be used if you're just converting
2929ef1f84bSDavid du Colombier * a delta.
2939ef1f84bSDavid du Colombier */
2949ef1f84bSDavid du Colombier ulong
tk2ms(ulong ticks)2959ef1f84bSDavid du Colombier tk2ms(ulong ticks)
2969ef1f84bSDavid du Colombier {
2979ef1f84bSDavid du Colombier uvlong t, hz;
2989ef1f84bSDavid du Colombier
2999ef1f84bSDavid du Colombier t = ticks;
3009ef1f84bSDavid du Colombier hz = HZ;
3019ef1f84bSDavid du Colombier t *= 1000L;
3029ef1f84bSDavid du Colombier t = t/hz;
3039ef1f84bSDavid du Colombier ticks = t;
3049ef1f84bSDavid du Colombier return ticks;
3059ef1f84bSDavid du Colombier }
3069ef1f84bSDavid du Colombier
3079ef1f84bSDavid du Colombier ulong
ms2tk(ulong ms)3089ef1f84bSDavid du Colombier ms2tk(ulong ms)
3099ef1f84bSDavid du Colombier {
3109ef1f84bSDavid du Colombier /* avoid overflows at the cost of precision */
3119ef1f84bSDavid du Colombier if(ms >= 1000000000/HZ)
3129ef1f84bSDavid du Colombier return (ms/1000)*HZ;
3139ef1f84bSDavid du Colombier return (ms*HZ+500)/1000;
3149ef1f84bSDavid du Colombier }
315