xref: /plan9-contrib/sys/src/9k/port/portclock.c (revision 406c76facc4b13aa2a55454bf4091aab9f03da22)
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