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"10 struct Timers11 {12 Lock;13 Timer *head;14 };16 static Timers timers[MAXMACH];18 ulong intrcount[MAXMACH];19 ulong fcallcount[MAXMACH];21 static vlong22 tadd(Timers *tt, Timer *nt)23 {24 Timer *t, **last;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 else48 nt->twhen = fastticks(nil);49 }50 nt->twhen += ns2fastticks(nt->tns);51 break;52 }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 }66 static uvlong67 tdel(Timer *dt)68 {70 Timer *t, **last;71 Timers *tt;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 }89 /* add or modify a timer */90 void91 timeradd(Timer *nt)92 {93 Timers *tt;94 vlong when;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 }113 void114 timerdel(Timer *dt)115 {116 Mach *mp;117 Timers *tt;118 uvlong when;120 /* avoid Tperiodic getting re-added */121 dt->tmode = Trelative;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);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 }143 void144 hzclock(Ureg *ur)145 {146 m->ticks++;147 if(m->proc)148 m->proc->pc = ur->pc;150 if(m->flushmmu){151 if(up)152 flushmmu();153 m->flushmmu = 0;154 }156 accounttime();157 kmapinval();159 if(kproftimer != nil)160 kproftimer(ur->pc);162 if(active.machs[m->machno] == 0)163 return;165 if(active.exiting)166 exit(0);168 if(m->machno == 0)169 checkalarms();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 }179 hzsched(); /* in proc.c */180 }181 }183 void184 timerintr(Ureg *u, Tval)185 {186 Timer *t;187 Timers *tt;188 uvlong when, now;189 int callhzclock;190 static int sofar;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 t200 * requires tdel(t) and this must be done with a201 * lock to tt held. We have tt, so the tdel will202 * wait until we're done203 */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 else221 callhzclock++;222 t->tactive = nil;223 ilock(tt);224 if(t->tmode == Tperiodic)225 tadd(tt, t);226 }227 iunlock(tt);228 }230 void231 timersinit(void)232 {233 Timer *t;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 }249 Timer*250 addclock0link(void (*f)(void), int ms)251 {252 Timer *nt;253 uvlong when;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;266 ilock(&timers[0]);267 when = tadd(&timers[0], nt);268 if(when)269 timerset(when);270 iunlock(&timers[0]);271 return nt;272 }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 converting277 * a delta.278 */279 ulong280 tk2ms(ulong ticks)281 {282 uvlong t, hz;284 t = ticks;285 hz = HZ;286 t *= 1000L;287 t = t/hz;288 ticks = t;289 return ticks;290 }292 ulong293 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 }