xref: /plan9/sys/src/9/port/portclock.c (revision fccc105cf4fc559ec4cc3ef19785ba2a6b42c8b0)
180ee5cbfSDavid du Colombier #include "u.h"
280ee5cbfSDavid du Colombier #include "../port/lib.h"
380ee5cbfSDavid du Colombier #include "mem.h"
480ee5cbfSDavid du Colombier #include "dat.h"
580ee5cbfSDavid du Colombier #include "fns.h"
680ee5cbfSDavid du Colombier #include "io.h"
780ee5cbfSDavid du Colombier #include "ureg.h"
8aa72973aSDavid du Colombier #include "../port/error.h"
980ee5cbfSDavid du Colombier 
10*27acba7cSDavid du Colombier enum {
11*27acba7cSDavid du Colombier 	Maxtimerloops = 20*1000,
12*27acba7cSDavid du Colombier };
13*27acba7cSDavid du Colombier 
149a747e4fSDavid du Colombier struct Timers
1580ee5cbfSDavid du Colombier {
169a747e4fSDavid du Colombier 	Lock;
179a747e4fSDavid du Colombier 	Timer	*head;
189a747e4fSDavid du Colombier };
1980ee5cbfSDavid du Colombier 
209a747e4fSDavid du Colombier static Timers timers[MAXMACH];
21*27acba7cSDavid du Colombier static int timersinited;
229a747e4fSDavid du Colombier 
239a747e4fSDavid du Colombier ulong intrcount[MAXMACH];
249a747e4fSDavid du Colombier ulong fcallcount[MAXMACH];
259a747e4fSDavid du Colombier 
262cca75a1SDavid du Colombier static vlong
tadd(Timers * tt,Timer * nt)2728495efeSDavid du Colombier tadd(Timers *tt, Timer *nt)
289a747e4fSDavid du Colombier {
29e288d156SDavid du Colombier 	Timer *t, **last;
309a747e4fSDavid du Colombier 
31e288d156SDavid du Colombier 	/* Called with tt locked */
32e288d156SDavid du Colombier 	assert(nt->tt == nil);
33e288d156SDavid du Colombier 	switch(nt->tmode){
34e288d156SDavid du Colombier 	default:
35e288d156SDavid du Colombier 		panic("timer");
369a747e4fSDavid du Colombier 		break;
37e288d156SDavid du Colombier 	case Trelative:
382cca75a1SDavid du Colombier 		if(nt->tns <= 0)
392cca75a1SDavid du Colombier 			nt->tns = 1;
4028495efeSDavid du Colombier 		nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
41e288d156SDavid du Colombier 		break;
42e288d156SDavid du Colombier 	case Tperiodic:
4328495efeSDavid du Colombier 		assert(nt->tns >= 100000);	/* At least 100 µs period */
44e288d156SDavid du Colombier 		if(nt->twhen == 0){
45d9306527SDavid du Colombier 			/* look for another timer at same frequency for combining */
46e288d156SDavid du Colombier 			for(t = tt->head; t; t = t->tnext){
47e288d156SDavid du Colombier 				if(t->tmode == Tperiodic && t->tns == nt->tns)
489a747e4fSDavid du Colombier 					break;
499a747e4fSDavid du Colombier 			}
50e288d156SDavid du Colombier 			if (t)
51e288d156SDavid du Colombier 				nt->twhen = t->twhen;
529a747e4fSDavid du Colombier 			else
5328495efeSDavid du Colombier 				nt->twhen = fastticks(nil);
5428495efeSDavid du Colombier 		}
5528495efeSDavid du Colombier 		nt->twhen += ns2fastticks(nt->tns);
56e288d156SDavid du Colombier 		break;
57e288d156SDavid du Colombier 	}
58e288d156SDavid du Colombier 
59e288d156SDavid du Colombier 	for(last = &tt->head; t = *last; last = &t->tnext){
60e288d156SDavid du Colombier 		if(t->twhen > nt->twhen)
61e288d156SDavid du Colombier 			break;
62e288d156SDavid du Colombier 	}
63e288d156SDavid du Colombier 	nt->tnext = *last;
64e288d156SDavid du Colombier 	*last = nt;
65e288d156SDavid du Colombier 	nt->tt = tt;
66e288d156SDavid du Colombier 	if(last == &tt->head)
67e288d156SDavid du Colombier 		return nt->twhen;
689a747e4fSDavid du Colombier 	return 0;
699a747e4fSDavid du Colombier }
709a747e4fSDavid du Colombier 
712e976735SDavid du Colombier static uvlong
tdel(Timer * dt)722e976735SDavid du Colombier tdel(Timer *dt)
732e976735SDavid du Colombier {
742e976735SDavid du Colombier 
752e976735SDavid du Colombier 	Timer *t, **last;
762e976735SDavid du Colombier 	Timers *tt;
772e976735SDavid du Colombier 
782e976735SDavid du Colombier 	tt = dt->tt;
792e976735SDavid du Colombier 	if (tt == nil)
802e976735SDavid du Colombier 		return 0;
812e976735SDavid du Colombier 	for(last = &tt->head; t = *last; last = &t->tnext){
822e976735SDavid du Colombier 		if(t == dt){
832e976735SDavid du Colombier 			assert(dt->tt);
842e976735SDavid du Colombier 			dt->tt = nil;
852e976735SDavid du Colombier 			*last = t->tnext;
862e976735SDavid du Colombier 			break;
872e976735SDavid du Colombier 		}
882e976735SDavid du Colombier 	}
892e976735SDavid du Colombier 	if(last == &tt->head && tt->head)
902e976735SDavid du Colombier 		return tt->head->twhen;
912e976735SDavid du Colombier 	return 0;
922e976735SDavid du Colombier }
932e976735SDavid du Colombier 
94e288d156SDavid du Colombier /* add or modify a timer */
959a747e4fSDavid du Colombier void
timeradd(Timer * nt)969a747e4fSDavid du Colombier timeradd(Timer *nt)
979a747e4fSDavid du Colombier {
989a747e4fSDavid du Colombier 	Timers *tt;
99da51d93aSDavid du Colombier 	vlong when;
1009a747e4fSDavid du Colombier 
1012e976735SDavid du Colombier 	/* Must lock Timer struct before Timers struct */
1022e976735SDavid du Colombier 	ilock(nt);
1032e976735SDavid du Colombier 	if(tt = nt->tt){
1042e976735SDavid du Colombier 		ilock(tt);
1052e976735SDavid du Colombier 		tdel(nt);
1062e976735SDavid du Colombier 		iunlock(tt);
1072e976735SDavid du Colombier 	}
1089a747e4fSDavid du Colombier 	tt = &timers[m->machno];
1099a747e4fSDavid du Colombier 	ilock(tt);
11028495efeSDavid du Colombier 	when = tadd(tt, nt);
1119a747e4fSDavid du Colombier 	if(when)
1129a747e4fSDavid du Colombier 		timerset(when);
1139a747e4fSDavid du Colombier 	iunlock(tt);
1142e976735SDavid du Colombier 	iunlock(nt);
11580ee5cbfSDavid du Colombier }
11680ee5cbfSDavid du Colombier 
1172e976735SDavid du Colombier 
11880ee5cbfSDavid du Colombier void
timerdel(Timer * dt)1199a747e4fSDavid du Colombier timerdel(Timer *dt)
12080ee5cbfSDavid du Colombier {
1219a747e4fSDavid du Colombier 	Timers *tt;
1222e976735SDavid du Colombier 	uvlong when;
12380ee5cbfSDavid du Colombier 
1242e976735SDavid du Colombier 	ilock(dt);
1252e976735SDavid du Colombier 	if(tt = dt->tt){
1269a747e4fSDavid du Colombier 		ilock(tt);
1272e976735SDavid du Colombier 		when = tdel(dt);
1282e976735SDavid du Colombier 		if(when && tt == &timers[m->machno])
129e288d156SDavid du Colombier 			timerset(tt->head->twhen);
1309a747e4fSDavid du Colombier 		iunlock(tt);
1319a747e4fSDavid du Colombier 	}
1322e976735SDavid du Colombier 	iunlock(dt);
133e288d156SDavid du Colombier }
1349a747e4fSDavid du Colombier 
1359a747e4fSDavid du Colombier void
hzclock(Ureg * ur)1369a747e4fSDavid du Colombier hzclock(Ureg *ur)
1379a747e4fSDavid du Colombier {
13880ee5cbfSDavid du Colombier 	m->ticks++;
13980ee5cbfSDavid du Colombier 	if(m->proc)
14080ee5cbfSDavid du Colombier 		m->proc->pc = ur->pc;
14180ee5cbfSDavid du Colombier 
1429a747e4fSDavid du Colombier 	if(m->flushmmu){
1439a747e4fSDavid du Colombier 		if(up)
1449a747e4fSDavid du Colombier 			flushmmu();
1459a747e4fSDavid du Colombier 		m->flushmmu = 0;
1469a747e4fSDavid du Colombier 	}
14780ee5cbfSDavid du Colombier 
14880ee5cbfSDavid du Colombier 	accounttime();
1499a747e4fSDavid du Colombier 	kmapinval();
15080ee5cbfSDavid du Colombier 
15180ee5cbfSDavid du Colombier 	if(kproftimer != nil)
15280ee5cbfSDavid du Colombier 		kproftimer(ur->pc);
15380ee5cbfSDavid du Colombier 
15480ee5cbfSDavid du Colombier 	if((active.machs&(1<<m->machno)) == 0)
15580ee5cbfSDavid du Colombier 		return;
15680ee5cbfSDavid du Colombier 
157e288d156SDavid du Colombier 	if(active.exiting) {
15880ee5cbfSDavid du Colombier 		print("someone's exiting\n");
15980ee5cbfSDavid du Colombier 		exit(0);
16080ee5cbfSDavid du Colombier 	}
16180ee5cbfSDavid du Colombier 
16280ee5cbfSDavid du Colombier 	checkalarms();
1639a747e4fSDavid du Colombier 
164e288d156SDavid du Colombier 	if(up && up->state == Running)
165a6a9e072SDavid du Colombier 		hzsched();	/* in proc.c */
1669a747e4fSDavid du Colombier }
1679a747e4fSDavid du Colombier 
1689a747e4fSDavid du Colombier void
timerintr(Ureg * u,Tval)169ea58ad6fSDavid du Colombier timerintr(Ureg *u, Tval)
1709a747e4fSDavid du Colombier {
1719a747e4fSDavid du Colombier 	Timer *t;
1729a747e4fSDavid du Colombier 	Timers *tt;
1739a747e4fSDavid du Colombier 	uvlong when, now;
174*27acba7cSDavid du Colombier 	int count, callhzclock;
1753ff48bf5SDavid du Colombier 
1769a747e4fSDavid du Colombier 	intrcount[m->machno]++;
1779a747e4fSDavid du Colombier 	callhzclock = 0;
1789a747e4fSDavid du Colombier 	tt = &timers[m->machno];
1799a747e4fSDavid du Colombier 	now = fastticks(nil);
180*27acba7cSDavid du Colombier 	if(now == 0)
181*27acba7cSDavid du Colombier 		panic("timerintr: zero fastticks()");
1829a747e4fSDavid du Colombier 	ilock(tt);
183*27acba7cSDavid du Colombier 	count = Maxtimerloops;
184*27acba7cSDavid du Colombier 	while((t = tt->head) != nil){
1852e976735SDavid du Colombier 		/*
1862e976735SDavid du Colombier 		 * No need to ilock t here: any manipulation of t
1872e976735SDavid du Colombier 		 * requires tdel(t) and this must be done with a
1882e976735SDavid du Colombier 		 * lock to tt held.  We have tt, so the tdel will
1892e976735SDavid du Colombier 		 * wait until we're done
1902e976735SDavid du Colombier 		 */
191e288d156SDavid du Colombier 		when = t->twhen;
1929a747e4fSDavid du Colombier 		if(when > now){
1939a747e4fSDavid du Colombier 			timerset(when);
194ef9eff0bSDavid du Colombier 			iunlock(tt);
1959a747e4fSDavid du Colombier 			if(callhzclock)
1969a747e4fSDavid du Colombier 				hzclock(u);
1979a747e4fSDavid du Colombier 			return;
1989a747e4fSDavid du Colombier 		}
199e288d156SDavid du Colombier 		tt->head = t->tnext;
200e288d156SDavid du Colombier 		assert(t->tt == tt);
201e288d156SDavid du Colombier 		t->tt = nil;
2029a747e4fSDavid du Colombier 		fcallcount[m->machno]++;
2039a747e4fSDavid du Colombier 		iunlock(tt);
20428495efeSDavid du Colombier 		if(t->tf)
205e288d156SDavid du Colombier 			(*t->tf)(u, t);
20628495efeSDavid du Colombier 		else
2079a747e4fSDavid du Colombier 			callhzclock++;
2089a747e4fSDavid du Colombier 		ilock(tt);
20928495efeSDavid du Colombier 		if(t->tmode == Tperiodic)
21028495efeSDavid du Colombier 			tadd(tt, t);
211*27acba7cSDavid du Colombier 		if (--count <= 0) {
212*27acba7cSDavid du Colombier 			count = Maxtimerloops;
213*27acba7cSDavid du Colombier 			iprint("timerintr: probably stuck in while loop; "
214*27acba7cSDavid du Colombier 				"scrutinise clock.c or use faster cycle "
215*27acba7cSDavid du Colombier 				"counter\n");
216*27acba7cSDavid du Colombier 		}
2179a747e4fSDavid du Colombier 	}
2189a747e4fSDavid du Colombier 	iunlock(tt);
2199a747e4fSDavid du Colombier }
2209a747e4fSDavid du Colombier 
2219a747e4fSDavid du Colombier void
timersinit(void)2229a747e4fSDavid du Colombier timersinit(void)
2239a747e4fSDavid du Colombier {
2249a747e4fSDavid du Colombier 	Timer *t;
2259a747e4fSDavid du Colombier 
2269acf0835SDavid du Colombier 	/*
227fc567a6bSDavid du Colombier 	 * T->tf == nil means the HZ clock for this processor.
2289acf0835SDavid du Colombier 	 */
229*27acba7cSDavid du Colombier 	timersinited = 1;
230e288d156SDavid du Colombier 	todinit();
2319a747e4fSDavid du Colombier 	t = malloc(sizeof(*t));
232aa72973aSDavid du Colombier 	if(t == nil)
233aa72973aSDavid du Colombier 		error(Enomem);
234e288d156SDavid du Colombier 	t->tmode = Tperiodic;
235e288d156SDavid du Colombier 	t->tt = nil;
236e288d156SDavid du Colombier 	t->tns = 1000000000/HZ;
237e288d156SDavid du Colombier 	t->tf = nil;
2389a747e4fSDavid du Colombier 	timeradd(t);
2399a747e4fSDavid du Colombier }
2409a747e4fSDavid du Colombier 
2415243b8d1SDavid du Colombier Timer*
addclock0link(void (* f)(void),int ms)242d9306527SDavid du Colombier addclock0link(void (*f)(void), int ms)
2439a747e4fSDavid du Colombier {
2449a747e4fSDavid du Colombier 	Timer *nt;
24528495efeSDavid du Colombier 	uvlong when;
2469a747e4fSDavid du Colombier 
247*27acba7cSDavid du Colombier 	if(!timersinited)
248*27acba7cSDavid du Colombier 		panic("addclock0link: timersinit not called yet");
249e288d156SDavid du Colombier 	/* Synchronize to hztimer if ms is 0 */
2509a747e4fSDavid du Colombier 	nt = malloc(sizeof(Timer));
251aa72973aSDavid du Colombier 	if(nt == nil)
252aa72973aSDavid du Colombier 		error(Enomem);
253d9306527SDavid du Colombier 	if(ms == 0)
254d9306527SDavid du Colombier 		ms = 1000/HZ;
255e288d156SDavid du Colombier 	nt->tns = (vlong)ms*1000000LL;
256e288d156SDavid du Colombier 	nt->tmode = Tperiodic;
257e288d156SDavid du Colombier 	nt->tt = nil;
258e288d156SDavid du Colombier 	nt->tf = (void (*)(Ureg*, Timer*))f;
259e288d156SDavid du Colombier 
2609a747e4fSDavid du Colombier 	ilock(&timers[0]);
26128495efeSDavid du Colombier 	when = tadd(&timers[0], nt);
26228495efeSDavid du Colombier 	if(when)
26328495efeSDavid du Colombier 		timerset(when);
2649a747e4fSDavid du Colombier 	iunlock(&timers[0]);
2655243b8d1SDavid du Colombier 	return nt;
26680ee5cbfSDavid du Colombier }
2673ff48bf5SDavid du Colombier 
2683ff48bf5SDavid du Colombier /*
2693ff48bf5SDavid du Colombier  *  This tk2ms avoids overflows that the macro version is prone to.
2703ff48bf5SDavid du Colombier  *  It is a LOT slower so shouldn't be used if you're just converting
2713ff48bf5SDavid du Colombier  *  a delta.
2723ff48bf5SDavid du Colombier  */
2733ff48bf5SDavid du Colombier ulong
tk2ms(ulong ticks)2743ff48bf5SDavid du Colombier tk2ms(ulong ticks)
2753ff48bf5SDavid du Colombier {
2763ff48bf5SDavid du Colombier 	uvlong t, hz;
2773ff48bf5SDavid du Colombier 
2783ff48bf5SDavid du Colombier 	t = ticks;
2793ff48bf5SDavid du Colombier 	hz = HZ;
2803ff48bf5SDavid du Colombier 	t *= 1000L;
2813ff48bf5SDavid du Colombier 	t = t/hz;
2823ff48bf5SDavid du Colombier 	ticks = t;
2833ff48bf5SDavid du Colombier 	return ticks;
2843ff48bf5SDavid du Colombier }
2853ff48bf5SDavid du Colombier 
2863ff48bf5SDavid du Colombier ulong
ms2tk(ulong ms)2873ff48bf5SDavid du Colombier ms2tk(ulong ms)
2883ff48bf5SDavid du Colombier {
2893ff48bf5SDavid du Colombier 	/* avoid overflows at the cost of precision */
2903ff48bf5SDavid du Colombier 	if(ms >= 1000000000/HZ)
2913ff48bf5SDavid du Colombier 		return (ms/1000)*HZ;
2923ff48bf5SDavid du Colombier 	return (ms*HZ+500)/1000;
2933ff48bf5SDavid du Colombier }
294