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