diff options
Diffstat (limited to 'os/port/portclock.c')
| -rw-r--r-- | os/port/portclock.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/os/port/portclock.c b/os/port/portclock.c new file mode 100644 index 00000000..04d1b110 --- /dev/null +++ b/os/port/portclock.c @@ -0,0 +1,277 @@ +#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 uvlong +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: + assert(nt->tns > 0); + nt->twhen = fastticks(nil) + ns2fastticks(nt->tns); + break; + case Tabsolute: + nt->twhen = tod2fastticks(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; + + if (nt->tmode == Tabsolute){ + when = todget(nil); + if (nt->tns <= when){ + // if (nt->tns + MS2NS(10) <= when) /* small deviations will happen */ + // print("timeradd (%lld %lld) %lld too early 0x%lux\n", + // when, nt->tns, when - nt->tns, getcallerpc(&nt)); + nt->tns = 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; + + kmapinval(); + + if(kproftick != nil) + kproftick(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){ + if(anyready()){ + sched(); + splhi(); + } + } +} + +void +timerintr(Ureg *u, uvlong) +{ + 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; + + 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; +} |
