diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/portclock.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/portclock.c')
-rwxr-xr-x | sys/src/9/port/portclock.c | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/sys/src/9/port/portclock.c b/sys/src/9/port/portclock.c new file mode 100755 index 000000000..624283d3c --- /dev/null +++ b/sys/src/9/port/portclock.c @@ -0,0 +1,272 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" + +struct Timers +{ + Lock; + Timer *head; +}; + +static Timers timers[MAXMACH]; + +ulong intrcount[MAXMACH]; +ulong fcallcount[MAXMACH]; + +static vlong +tadd(Timers *tt, Timer *nt) +{ + Timer *t, **last; + + /* Called with tt locked */ + assert(nt->tt == nil); + switch(nt->tmode){ + default: + panic("timer"); + break; + case Trelative: + if(nt->tns <= 0) + nt->tns = 1; + nt->twhen = fastticks(nil) + ns2fastticks(nt->tns); + break; + case Tperiodic: + assert(nt->tns >= 100000); /* At least 100 µs period */ + if(nt->twhen == 0){ + /* look for another timer at same frequency for combining */ + for(t = tt->head; t; t = t->tnext){ + if(t->tmode == Tperiodic && t->tns == nt->tns) + break; + } + if (t) + nt->twhen = t->twhen; + else + nt->twhen = fastticks(nil); + } + nt->twhen += ns2fastticks(nt->tns); + break; + } + + for(last = &tt->head; t = *last; last = &t->tnext){ + if(t->twhen > nt->twhen) + break; + } + nt->tnext = *last; + *last = nt; + nt->tt = tt; + if(last == &tt->head) + return nt->twhen; + return 0; +} + +static uvlong +tdel(Timer *dt) +{ + + Timer *t, **last; + Timers *tt; + + tt = dt->tt; + if (tt == nil) + return 0; + for(last = &tt->head; t = *last; last = &t->tnext){ + if(t == dt){ + assert(dt->tt); + dt->tt = nil; + *last = t->tnext; + break; + } + } + if(last == &tt->head && tt->head) + return tt->head->twhen; + return 0; +} + +/* add or modify a timer */ +void +timeradd(Timer *nt) +{ + Timers *tt; + vlong when; + + /* Must lock Timer struct before Timers struct */ + ilock(nt); + if(tt = nt->tt){ + ilock(tt); + tdel(nt); + iunlock(tt); + } + tt = &timers[m->machno]; + ilock(tt); + when = tadd(tt, nt); + if(when) + timerset(when); + iunlock(tt); + iunlock(nt); +} + + +void +timerdel(Timer *dt) +{ + Timers *tt; + uvlong when; + + ilock(dt); + if(tt = dt->tt){ + ilock(tt); + when = tdel(dt); + if(when && tt == &timers[m->machno]) + timerset(tt->head->twhen); + iunlock(tt); + } + iunlock(dt); +} + +void +hzclock(Ureg *ur) +{ + m->ticks++; + if(m->proc) + m->proc->pc = ur->pc; + + if(m->flushmmu){ + if(up) + flushmmu(); + m->flushmmu = 0; + } + + accounttime(); + kmapinval(); + + if(kproftimer != nil) + kproftimer(ur->pc); + + if((active.machs&(1<<m->machno)) == 0) + return; + + if(active.exiting) { + print("someone's exiting\n"); + exit(0); + } + + checkalarms(); + + if(up && up->state == Running) + hzsched(); /* in proc.c */ +} + +void +timerintr(Ureg *u, Tval) +{ + Timer *t; + Timers *tt; + uvlong when, now; + int callhzclock; + static int sofar; + + intrcount[m->machno]++; + callhzclock = 0; + tt = &timers[m->machno]; + now = fastticks(nil); + ilock(tt); + while(t = tt->head){ + /* + * No need to ilock t here: any manipulation of t + * requires tdel(t) and this must be done with a + * lock to tt held. We have tt, so the tdel will + * wait until we're done + */ + when = t->twhen; + if(when > now){ + timerset(when); + iunlock(tt); + if(callhzclock) + hzclock(u); + return; + } + tt->head = t->tnext; + assert(t->tt == tt); + t->tt = nil; + fcallcount[m->machno]++; + iunlock(tt); + if(t->tf) + (*t->tf)(u, t); + else + callhzclock++; + ilock(tt); + if(t->tmode == Tperiodic) + tadd(tt, t); + } + iunlock(tt); +} + +void +timersinit(void) +{ + Timer *t; + + /* + * T->tf == nil means the HZ clock for this processor. + */ + todinit(); + t = malloc(sizeof(*t)); + t->tmode = Tperiodic; + t->tt = nil; + t->tns = 1000000000/HZ; + t->tf = nil; + timeradd(t); +} + +Timer* +addclock0link(void (*f)(void), int ms) +{ + Timer *nt; + uvlong when; + + /* Synchronize to hztimer if ms is 0 */ + nt = malloc(sizeof(Timer)); + if(ms == 0) + ms = 1000/HZ; + nt->tns = (vlong)ms*1000000LL; + nt->tmode = Tperiodic; + nt->tt = nil; + nt->tf = (void (*)(Ureg*, Timer*))f; + + ilock(&timers[0]); + when = tadd(&timers[0], nt); + if(when) + timerset(when); + iunlock(&timers[0]); + return nt; +} + +/* + * This tk2ms avoids overflows that the macro version is prone to. + * It is a LOT slower so shouldn't be used if you're just converting + * a delta. + */ +ulong +tk2ms(ulong ticks) +{ + uvlong t, hz; + + t = ticks; + hz = HZ; + t *= 1000L; + t = t/hz; + ticks = t; + return ticks; +} + +ulong +ms2tk(ulong ms) +{ + /* avoid overflows at the cost of precision */ + if(ms >= 1000000000/HZ) + return (ms/1000)*HZ; + return (ms*HZ+500)/1000; +} |