xref: /plan9/sys/src/9/port/portclock.c (revision fccc105cf4fc559ec4cc3ef19785ba2a6b42c8b0)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "ureg.h"
8 #include "../port/error.h"
9 
10 enum {
11 	Maxtimerloops = 20*1000,
12 };
13 
14 struct Timers
15 {
16 	Lock;
17 	Timer	*head;
18 };
19 
20 static Timers timers[MAXMACH];
21 static int timersinited;
22 
23 ulong intrcount[MAXMACH];
24 ulong fcallcount[MAXMACH];
25 
26 static vlong
tadd(Timers * tt,Timer * nt)27 tadd(Timers *tt, Timer *nt)
28 {
29 	Timer *t, **last;
30 
31 	/* Called with tt locked */
32 	assert(nt->tt == nil);
33 	switch(nt->tmode){
34 	default:
35 		panic("timer");
36 		break;
37 	case Trelative:
38 		if(nt->tns <= 0)
39 			nt->tns = 1;
40 		nt->twhen = fastticks(nil) + ns2fastticks(nt->tns);
41 		break;
42 	case Tperiodic:
43 		assert(nt->tns >= 100000);	/* At least 100 µs period */
44 		if(nt->twhen == 0){
45 			/* look for another timer at same frequency for combining */
46 			for(t = tt->head; t; t = t->tnext){
47 				if(t->tmode == Tperiodic && t->tns == nt->tns)
48 					break;
49 			}
50 			if (t)
51 				nt->twhen = t->twhen;
52 			else
53 				nt->twhen = fastticks(nil);
54 		}
55 		nt->twhen += ns2fastticks(nt->tns);
56 		break;
57 	}
58 
59 	for(last = &tt->head; t = *last; last = &t->tnext){
60 		if(t->twhen > nt->twhen)
61 			break;
62 	}
63 	nt->tnext = *last;
64 	*last = nt;
65 	nt->tt = tt;
66 	if(last == &tt->head)
67 		return nt->twhen;
68 	return 0;
69 }
70 
71 static uvlong
tdel(Timer * dt)72 tdel(Timer *dt)
73 {
74 
75 	Timer *t, **last;
76 	Timers *tt;
77 
78 	tt = dt->tt;
79 	if (tt == nil)
80 		return 0;
81 	for(last = &tt->head; t = *last; last = &t->tnext){
82 		if(t == dt){
83 			assert(dt->tt);
84 			dt->tt = nil;
85 			*last = t->tnext;
86 			break;
87 		}
88 	}
89 	if(last == &tt->head && tt->head)
90 		return tt->head->twhen;
91 	return 0;
92 }
93 
94 /* add or modify a timer */
95 void
timeradd(Timer * nt)96 timeradd(Timer *nt)
97 {
98 	Timers *tt;
99 	vlong when;
100 
101 	/* Must lock Timer struct before Timers struct */
102 	ilock(nt);
103 	if(tt = nt->tt){
104 		ilock(tt);
105 		tdel(nt);
106 		iunlock(tt);
107 	}
108 	tt = &timers[m->machno];
109 	ilock(tt);
110 	when = tadd(tt, nt);
111 	if(when)
112 		timerset(when);
113 	iunlock(tt);
114 	iunlock(nt);
115 }
116 
117 
118 void
timerdel(Timer * dt)119 timerdel(Timer *dt)
120 {
121 	Timers *tt;
122 	uvlong when;
123 
124 	ilock(dt);
125 	if(tt = dt->tt){
126 		ilock(tt);
127 		when = tdel(dt);
128 		if(when && tt == &timers[m->machno])
129 			timerset(tt->head->twhen);
130 		iunlock(tt);
131 	}
132 	iunlock(dt);
133 }
134 
135 void
hzclock(Ureg * ur)136 hzclock(Ureg *ur)
137 {
138 	m->ticks++;
139 	if(m->proc)
140 		m->proc->pc = ur->pc;
141 
142 	if(m->flushmmu){
143 		if(up)
144 			flushmmu();
145 		m->flushmmu = 0;
146 	}
147 
148 	accounttime();
149 	kmapinval();
150 
151 	if(kproftimer != nil)
152 		kproftimer(ur->pc);
153 
154 	if((active.machs&(1<<m->machno)) == 0)
155 		return;
156 
157 	if(active.exiting) {
158 		print("someone's exiting\n");
159 		exit(0);
160 	}
161 
162 	checkalarms();
163 
164 	if(up && up->state == Running)
165 		hzsched();	/* in proc.c */
166 }
167 
168 void
timerintr(Ureg * u,Tval)169 timerintr(Ureg *u, Tval)
170 {
171 	Timer *t;
172 	Timers *tt;
173 	uvlong when, now;
174 	int count, callhzclock;
175 
176 	intrcount[m->machno]++;
177 	callhzclock = 0;
178 	tt = &timers[m->machno];
179 	now = fastticks(nil);
180 	if(now == 0)
181 		panic("timerintr: zero fastticks()");
182 	ilock(tt);
183 	count = Maxtimerloops;
184 	while((t = tt->head) != nil){
185 		/*
186 		 * No need to ilock t here: any manipulation of t
187 		 * requires tdel(t) and this must be done with a
188 		 * lock to tt held.  We have tt, so the tdel will
189 		 * wait until we're done
190 		 */
191 		when = t->twhen;
192 		if(when > now){
193 			timerset(when);
194 			iunlock(tt);
195 			if(callhzclock)
196 				hzclock(u);
197 			return;
198 		}
199 		tt->head = t->tnext;
200 		assert(t->tt == tt);
201 		t->tt = nil;
202 		fcallcount[m->machno]++;
203 		iunlock(tt);
204 		if(t->tf)
205 			(*t->tf)(u, t);
206 		else
207 			callhzclock++;
208 		ilock(tt);
209 		if(t->tmode == Tperiodic)
210 			tadd(tt, t);
211 		if (--count <= 0) {
212 			count = Maxtimerloops;
213 			iprint("timerintr: probably stuck in while loop; "
214 				"scrutinise clock.c or use faster cycle "
215 				"counter\n");
216 		}
217 	}
218 	iunlock(tt);
219 }
220 
221 void
timersinit(void)222 timersinit(void)
223 {
224 	Timer *t;
225 
226 	/*
227 	 * T->tf == nil means the HZ clock for this processor.
228 	 */
229 	timersinited = 1;
230 	todinit();
231 	t = malloc(sizeof(*t));
232 	if(t == nil)
233 		error(Enomem);
234 	t->tmode = Tperiodic;
235 	t->tt = nil;
236 	t->tns = 1000000000/HZ;
237 	t->tf = nil;
238 	timeradd(t);
239 }
240 
241 Timer*
addclock0link(void (* f)(void),int ms)242 addclock0link(void (*f)(void), int ms)
243 {
244 	Timer *nt;
245 	uvlong when;
246 
247 	if(!timersinited)
248 		panic("addclock0link: timersinit not called yet");
249 	/* Synchronize to hztimer if ms is 0 */
250 	nt = malloc(sizeof(Timer));
251 	if(nt == nil)
252 		error(Enomem);
253 	if(ms == 0)
254 		ms = 1000/HZ;
255 	nt->tns = (vlong)ms*1000000LL;
256 	nt->tmode = Tperiodic;
257 	nt->tt = nil;
258 	nt->tf = (void (*)(Ureg*, Timer*))f;
259 
260 	ilock(&timers[0]);
261 	when = tadd(&timers[0], nt);
262 	if(when)
263 		timerset(when);
264 	iunlock(&timers[0]);
265 	return nt;
266 }
267 
268 /*
269  *  This tk2ms avoids overflows that the macro version is prone to.
270  *  It is a LOT slower so shouldn't be used if you're just converting
271  *  a delta.
272  */
273 ulong
tk2ms(ulong ticks)274 tk2ms(ulong ticks)
275 {
276 	uvlong t, hz;
277 
278 	t = ticks;
279 	hz = HZ;
280 	t *= 1000L;
281 	t = t/hz;
282 	ticks = t;
283 	return ticks;
284 }
285 
286 ulong
ms2tk(ulong ms)287 ms2tk(ulong ms)
288 {
289 	/* avoid overflows at the cost of precision */
290 	if(ms >= 1000000000/HZ)
291 		return (ms/1000)*HZ;
292 	return (ms*HZ+500)/1000;
293 }
294