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