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