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