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