changelog shortlog tags branches changeset files revisions annotate raw help

Mercurial > hg > plan9front / sys/src/9/port/portclock.c

changeset 6937: 62ba89f5ffb8
parent: 381f1cb08002
child: 2109eab7793b
author: cinap_lenrek@felloff.net
date: Wed, 05 Dec 2018 01:43:19 +0100
permissions: -rw-r--r--
description: kernel: fix tprof on multiprocessor

segclock() has to be called from hzclock(), otherwise
only processes running on cpu0 would catche the interrupt
and the time delta would be wrong.

lock the segment when allocating Seg->profile as
profile ctl might be issued from multiple processes.
Proc->debug qlock is not sufficient.

Seg->profile can never be freed or reallocated once
set as the timer interrupt accesses it without any
locking.
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)
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  static int sofar;
191 
192  intrcount[m->machno]++;
193  callhzclock = 0;
194  tt = &timers[m->machno];
195  now = fastticks(nil);
196  ilock(tt);
197  while(t = tt->head){
198  /*
199  * No need to ilock t here: any manipulation of t
200  * requires tdel(t) and this must be done with a
201  * lock to tt held. We have tt, so the tdel will
202  * wait until we're done
203  */
204  when = t->twhen;
205  if(when > now){
206  timerset(when);
207  iunlock(tt);
208  if(callhzclock)
209  hzclock(u);
210  return;
211  }
212  tt->head = t->tnext;
213  assert(t->tt == tt);
214  t->tt = nil;
215  t->tactive = MACHP(m->machno);
216  fcallcount[m->machno]++;
217  iunlock(tt);
218  if(t->tf)
219  (*t->tf)(u, t);
220  else
221  callhzclock++;
222  t->tactive = nil;
223  ilock(tt);
224  if(t->tmode == Tperiodic)
225  tadd(tt, t);
226  }
227  iunlock(tt);
228 }
229 
230 void
231 timersinit(void)
232 {
233  Timer *t;
234 
235  /*
236  * T->tf == nil means the HZ clock for this processor.
237  */
238  todinit();
239  t = malloc(sizeof(*t));
240  if(t == nil)
241  panic("timersinit: no memory for Timer");
242  t->tmode = Tperiodic;
243  t->tt = nil;
244  t->tns = 1000000000/HZ;
245  t->tf = nil;
246  timeradd(t);
247 }
248 
249 Timer*
250 addclock0link(void (*f)(void), int ms)
251 {
252  Timer *nt;
253  uvlong when;
254 
255  /* Synchronize to hztimer if ms is 0 */
256  nt = malloc(sizeof(Timer));
257  if(nt == nil)
258  panic("addclock0link: no memory for Timer");
259  if(ms == 0)
260  ms = 1000/HZ;
261  nt->tns = (vlong)ms*1000000LL;
262  nt->tmode = Tperiodic;
263  nt->tt = nil;
264  nt->tf = (void (*)(Ureg*, Timer*))f;
265 
266  ilock(&timers[0]);
267  when = tadd(&timers[0], nt);
268  if(when)
269  timerset(when);
270  iunlock(&timers[0]);
271  return nt;
272 }
273 
274 /*
275  * This tk2ms avoids overflows that the macro version is prone to.
276  * It is a LOT slower so shouldn't be used if you're just converting
277  * a delta.
278  */
279 ulong
280 tk2ms(ulong ticks)
281 {
282  uvlong t, hz;
283 
284  t = ticks;
285  hz = HZ;
286  t *= 1000L;
287  t = t/hz;
288  ticks = t;
289  return ticks;
290 }
291 
292 ulong
293 ms2tk(ulong ms)
294 {
295  /* avoid overflows at the cost of precision */
296  if(ms >= 1000000000/HZ)
297  return (ms/1000)*HZ;
298  return (ms*HZ+500)/1000;
299 }