1df8bae1dSRodney W. Grimes /*- 2df8bae1dSRodney W. Grimes * Copyright (c) 1982, 1986, 1991, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * (c) UNIX System Laboratories, Inc. 5df8bae1dSRodney W. Grimes * All or some portions of this file are derived from material licensed 6df8bae1dSRodney W. Grimes * to the University of California by American Telephone and Telegraph 7df8bae1dSRodney W. Grimes * Co. or Unix System Laboratories, Inc. and are reproduced herein with 8df8bae1dSRodney W. Grimes * the permission of UNIX System Laboratories, Inc. 9df8bae1dSRodney W. Grimes * 10df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 11df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 12df8bae1dSRodney W. Grimes * are met: 13df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 14df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 15df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 16df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 17df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 18df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 19df8bae1dSRodney W. Grimes * must display the following acknowledgement: 20df8bae1dSRodney W. Grimes * This product includes software developed by the University of 21df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 22df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 23df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 24df8bae1dSRodney W. Grimes * without specific prior written permission. 25df8bae1dSRodney W. Grimes * 26df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36df8bae1dSRodney W. Grimes * SUCH DAMAGE. 37df8bae1dSRodney W. Grimes * 38df8bae1dSRodney W. Grimes * @(#)kern_clock.c 8.5 (Berkeley) 1/21/94 39df8bae1dSRodney W. Grimes */ 40df8bae1dSRodney W. Grimes 41df8bae1dSRodney W. Grimes #include <sys/param.h> 42df8bae1dSRodney W. Grimes #include <sys/systm.h> 43df8bae1dSRodney W. Grimes #include <sys/dkstat.h> 44df8bae1dSRodney W. Grimes #include <sys/callout.h> 45df8bae1dSRodney W. Grimes #include <sys/kernel.h> 46df8bae1dSRodney W. Grimes #include <sys/proc.h> 47df8bae1dSRodney W. Grimes #include <sys/resourcevar.h> 48df8bae1dSRodney W. Grimes 49df8bae1dSRodney W. Grimes #include <machine/cpu.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes #ifdef GPROF 52df8bae1dSRodney W. Grimes #include <sys/gmon.h> 53df8bae1dSRodney W. Grimes #endif 54df8bae1dSRodney W. Grimes 55df8bae1dSRodney W. Grimes /* 56df8bae1dSRodney W. Grimes * Clock handling routines. 57df8bae1dSRodney W. Grimes * 58df8bae1dSRodney W. Grimes * This code is written to operate with two timers that run independently of 59df8bae1dSRodney W. Grimes * each other. The main clock, running hz times per second, is used to keep 60df8bae1dSRodney W. Grimes * track of real time. The second timer handles kernel and user profiling, 61df8bae1dSRodney W. Grimes * and does resource use estimation. If the second timer is programmable, 62df8bae1dSRodney W. Grimes * it is randomized to avoid aliasing between the two clocks. For example, 63df8bae1dSRodney W. Grimes * the randomization prevents an adversary from always giving up the cpu 64df8bae1dSRodney W. Grimes * just before its quantum expires. Otherwise, it would never accumulate 65df8bae1dSRodney W. Grimes * cpu ticks. The mean frequency of the second timer is stathz. 66df8bae1dSRodney W. Grimes * 67df8bae1dSRodney W. Grimes * If no second timer exists, stathz will be zero; in this case we drive 68df8bae1dSRodney W. Grimes * profiling and statistics off the main clock. This WILL NOT be accurate; 69df8bae1dSRodney W. Grimes * do not do it unless absolutely necessary. 70df8bae1dSRodney W. Grimes * 71df8bae1dSRodney W. Grimes * The statistics clock may (or may not) be run at a higher rate while 72df8bae1dSRodney W. Grimes * profiling. This profile clock runs at profhz. We require that profhz 73df8bae1dSRodney W. Grimes * be an integral multiple of stathz. 74df8bae1dSRodney W. Grimes * 75df8bae1dSRodney W. Grimes * If the statistics clock is running fast, it must be divided by the ratio 76df8bae1dSRodney W. Grimes * profhz/stathz for statistics. (For profiling, every tick counts.) 77df8bae1dSRodney W. Grimes */ 78df8bae1dSRodney W. Grimes 79df8bae1dSRodney W. Grimes /* 80df8bae1dSRodney W. Grimes * TODO: 81df8bae1dSRodney W. Grimes * allocate more timeout table slots when table overflows. 82df8bae1dSRodney W. Grimes */ 83df8bae1dSRodney W. Grimes 84df8bae1dSRodney W. Grimes /* 85df8bae1dSRodney W. Grimes * Bump a timeval by a small number of usec's. 86df8bae1dSRodney W. Grimes */ 87df8bae1dSRodney W. Grimes #define BUMPTIME(t, usec) { \ 88df8bae1dSRodney W. Grimes register volatile struct timeval *tp = (t); \ 89df8bae1dSRodney W. Grimes register long us; \ 90df8bae1dSRodney W. Grimes \ 91df8bae1dSRodney W. Grimes tp->tv_usec = us = tp->tv_usec + (usec); \ 92df8bae1dSRodney W. Grimes if (us >= 1000000) { \ 93df8bae1dSRodney W. Grimes tp->tv_usec = us - 1000000; \ 94df8bae1dSRodney W. Grimes tp->tv_sec++; \ 95df8bae1dSRodney W. Grimes } \ 96df8bae1dSRodney W. Grimes } 97df8bae1dSRodney W. Grimes 98df8bae1dSRodney W. Grimes int stathz; 99df8bae1dSRodney W. Grimes int profhz; 100df8bae1dSRodney W. Grimes int profprocs; 101df8bae1dSRodney W. Grimes int ticks; 102df8bae1dSRodney W. Grimes static int psdiv, pscnt; /* prof => stat divider */ 103df8bae1dSRodney W. Grimes int psratio; /* ratio: prof / stat */ 104df8bae1dSRodney W. Grimes 105df8bae1dSRodney W. Grimes volatile struct timeval time; 106df8bae1dSRodney W. Grimes volatile struct timeval mono_time; 107df8bae1dSRodney W. Grimes 108df8bae1dSRodney W. Grimes /* 109df8bae1dSRodney W. Grimes * Initialize clock frequencies and start both clocks running. 110df8bae1dSRodney W. Grimes */ 111df8bae1dSRodney W. Grimes void 112df8bae1dSRodney W. Grimes initclocks() 113df8bae1dSRodney W. Grimes { 114df8bae1dSRodney W. Grimes register int i; 115df8bae1dSRodney W. Grimes 116df8bae1dSRodney W. Grimes /* 117df8bae1dSRodney W. Grimes * Set divisors to 1 (normal case) and let the machine-specific 118df8bae1dSRodney W. Grimes * code do its bit. 119df8bae1dSRodney W. Grimes */ 120df8bae1dSRodney W. Grimes psdiv = pscnt = 1; 121df8bae1dSRodney W. Grimes cpu_initclocks(); 122df8bae1dSRodney W. Grimes 123df8bae1dSRodney W. Grimes /* 124df8bae1dSRodney W. Grimes * Compute profhz/stathz, and fix profhz if needed. 125df8bae1dSRodney W. Grimes */ 126df8bae1dSRodney W. Grimes i = stathz ? stathz : hz; 127df8bae1dSRodney W. Grimes if (profhz == 0) 128df8bae1dSRodney W. Grimes profhz = i; 129df8bae1dSRodney W. Grimes psratio = profhz / i; 130df8bae1dSRodney W. Grimes } 131df8bae1dSRodney W. Grimes 132df8bae1dSRodney W. Grimes /* 133df8bae1dSRodney W. Grimes * The real-time timer, interrupting hz times per second. 134df8bae1dSRodney W. Grimes */ 135df8bae1dSRodney W. Grimes void 136df8bae1dSRodney W. Grimes hardclock(frame) 137df8bae1dSRodney W. Grimes register struct clockframe *frame; 138df8bae1dSRodney W. Grimes { 139df8bae1dSRodney W. Grimes register struct callout *p1; 140df8bae1dSRodney W. Grimes register struct proc *p; 141df8bae1dSRodney W. Grimes register int delta, needsoft; 142df8bae1dSRodney W. Grimes extern int tickdelta; 143df8bae1dSRodney W. Grimes extern long timedelta; 144df8bae1dSRodney W. Grimes 145df8bae1dSRodney W. Grimes /* 146df8bae1dSRodney W. Grimes * Update real-time timeout queue. 147df8bae1dSRodney W. Grimes * At front of queue are some number of events which are ``due''. 148df8bae1dSRodney W. Grimes * The time to these is <= 0 and if negative represents the 149df8bae1dSRodney W. Grimes * number of ticks which have passed since it was supposed to happen. 150df8bae1dSRodney W. Grimes * The rest of the q elements (times > 0) are events yet to happen, 151df8bae1dSRodney W. Grimes * where the time for each is given as a delta from the previous. 152df8bae1dSRodney W. Grimes * Decrementing just the first of these serves to decrement the time 153df8bae1dSRodney W. Grimes * to all events. 154df8bae1dSRodney W. Grimes */ 155df8bae1dSRodney W. Grimes needsoft = 0; 156df8bae1dSRodney W. Grimes for (p1 = calltodo.c_next; p1 != NULL; p1 = p1->c_next) { 157df8bae1dSRodney W. Grimes if (--p1->c_time > 0) 158df8bae1dSRodney W. Grimes break; 159df8bae1dSRodney W. Grimes needsoft = 1; 160df8bae1dSRodney W. Grimes if (p1->c_time == 0) 161df8bae1dSRodney W. Grimes break; 162df8bae1dSRodney W. Grimes } 163df8bae1dSRodney W. Grimes 164df8bae1dSRodney W. Grimes p = curproc; 165df8bae1dSRodney W. Grimes if (p) { 166df8bae1dSRodney W. Grimes register struct pstats *pstats; 167df8bae1dSRodney W. Grimes 168df8bae1dSRodney W. Grimes /* 169df8bae1dSRodney W. Grimes * Run current process's virtual and profile time, as needed. 170df8bae1dSRodney W. Grimes */ 171df8bae1dSRodney W. Grimes pstats = p->p_stats; 172df8bae1dSRodney W. Grimes if (CLKF_USERMODE(frame) && 173df8bae1dSRodney W. Grimes timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) && 174df8bae1dSRodney W. Grimes itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0) 175df8bae1dSRodney W. Grimes psignal(p, SIGVTALRM); 176df8bae1dSRodney W. Grimes if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) && 177df8bae1dSRodney W. Grimes itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0) 178df8bae1dSRodney W. Grimes psignal(p, SIGPROF); 179df8bae1dSRodney W. Grimes } 180df8bae1dSRodney W. Grimes 181df8bae1dSRodney W. Grimes /* 182df8bae1dSRodney W. Grimes * If no separate statistics clock is available, run it from here. 183df8bae1dSRodney W. Grimes */ 184df8bae1dSRodney W. Grimes if (stathz == 0) 185df8bae1dSRodney W. Grimes statclock(frame); 186df8bae1dSRodney W. Grimes 187df8bae1dSRodney W. Grimes /* 188df8bae1dSRodney W. Grimes * Increment the time-of-day. The increment is just ``tick'' unless 189df8bae1dSRodney W. Grimes * we are still adjusting the clock; see adjtime(). 190df8bae1dSRodney W. Grimes */ 191df8bae1dSRodney W. Grimes ticks++; 192df8bae1dSRodney W. Grimes if (timedelta == 0) 193df8bae1dSRodney W. Grimes delta = tick; 194df8bae1dSRodney W. Grimes else { 195df8bae1dSRodney W. Grimes delta = tick + tickdelta; 196df8bae1dSRodney W. Grimes timedelta -= tickdelta; 197df8bae1dSRodney W. Grimes } 198df8bae1dSRodney W. Grimes BUMPTIME(&time, delta); 199df8bae1dSRodney W. Grimes BUMPTIME(&mono_time, delta); 200df8bae1dSRodney W. Grimes 201df8bae1dSRodney W. Grimes /* 202df8bae1dSRodney W. Grimes * Process callouts at a very low cpu priority, so we don't keep the 203df8bae1dSRodney W. Grimes * relatively high clock interrupt priority any longer than necessary. 204df8bae1dSRodney W. Grimes */ 205df8bae1dSRodney W. Grimes if (needsoft) { 206df8bae1dSRodney W. Grimes if (CLKF_BASEPRI(frame)) { 207df8bae1dSRodney W. Grimes /* 208df8bae1dSRodney W. Grimes * Save the overhead of a software interrupt; 209df8bae1dSRodney W. Grimes * it will happen as soon as we return, so do it now. 210df8bae1dSRodney W. Grimes */ 211df8bae1dSRodney W. Grimes (void)splsoftclock(); 212df8bae1dSRodney W. Grimes softclock(); 213df8bae1dSRodney W. Grimes } else 214df8bae1dSRodney W. Grimes setsoftclock(); 215df8bae1dSRodney W. Grimes } 216df8bae1dSRodney W. Grimes } 217df8bae1dSRodney W. Grimes 218df8bae1dSRodney W. Grimes /* 219df8bae1dSRodney W. Grimes * Software (low priority) clock interrupt. 220df8bae1dSRodney W. Grimes * Run periodic events from timeout queue. 221df8bae1dSRodney W. Grimes */ 222df8bae1dSRodney W. Grimes /*ARGSUSED*/ 223df8bae1dSRodney W. Grimes void 224df8bae1dSRodney W. Grimes softclock() 225df8bae1dSRodney W. Grimes { 226df8bae1dSRodney W. Grimes register struct callout *c; 227df8bae1dSRodney W. Grimes register void *arg; 228df8bae1dSRodney W. Grimes register void (*func) __P((void *)); 229df8bae1dSRodney W. Grimes register int s; 230df8bae1dSRodney W. Grimes 231df8bae1dSRodney W. Grimes s = splhigh(); 232df8bae1dSRodney W. Grimes while ((c = calltodo.c_next) != NULL && c->c_time <= 0) { 233df8bae1dSRodney W. Grimes func = c->c_func; 234df8bae1dSRodney W. Grimes arg = c->c_arg; 235df8bae1dSRodney W. Grimes calltodo.c_next = c->c_next; 236df8bae1dSRodney W. Grimes c->c_next = callfree; 237df8bae1dSRodney W. Grimes callfree = c; 238df8bae1dSRodney W. Grimes splx(s); 239df8bae1dSRodney W. Grimes (*func)(arg); 240df8bae1dSRodney W. Grimes (void) splhigh(); 241df8bae1dSRodney W. Grimes } 242df8bae1dSRodney W. Grimes splx(s); 243df8bae1dSRodney W. Grimes } 244df8bae1dSRodney W. Grimes 245df8bae1dSRodney W. Grimes /* 246df8bae1dSRodney W. Grimes * timeout -- 247df8bae1dSRodney W. Grimes * Execute a function after a specified length of time. 248df8bae1dSRodney W. Grimes * 249df8bae1dSRodney W. Grimes * untimeout -- 250df8bae1dSRodney W. Grimes * Cancel previous timeout function call. 251df8bae1dSRodney W. Grimes * 252df8bae1dSRodney W. Grimes * See AT&T BCI Driver Reference Manual for specification. This 253df8bae1dSRodney W. Grimes * implementation differs from that one in that no identification 254df8bae1dSRodney W. Grimes * value is returned from timeout, rather, the original arguments 255df8bae1dSRodney W. Grimes * to timeout are used to identify entries for untimeout. 256df8bae1dSRodney W. Grimes */ 257df8bae1dSRodney W. Grimes void 258df8bae1dSRodney W. Grimes timeout(ftn, arg, ticks) 259df8bae1dSRodney W. Grimes void (*ftn) __P((void *)); 260df8bae1dSRodney W. Grimes void *arg; 261df8bae1dSRodney W. Grimes register int ticks; 262df8bae1dSRodney W. Grimes { 263df8bae1dSRodney W. Grimes register struct callout *new, *p, *t; 264df8bae1dSRodney W. Grimes register int s; 265df8bae1dSRodney W. Grimes 266df8bae1dSRodney W. Grimes if (ticks <= 0) 267df8bae1dSRodney W. Grimes ticks = 1; 268df8bae1dSRodney W. Grimes 269df8bae1dSRodney W. Grimes /* Lock out the clock. */ 270df8bae1dSRodney W. Grimes s = splhigh(); 271df8bae1dSRodney W. Grimes 272df8bae1dSRodney W. Grimes /* Fill in the next free callout structure. */ 273df8bae1dSRodney W. Grimes if (callfree == NULL) 274df8bae1dSRodney W. Grimes panic("timeout table full"); 275df8bae1dSRodney W. Grimes new = callfree; 276df8bae1dSRodney W. Grimes callfree = new->c_next; 277df8bae1dSRodney W. Grimes new->c_arg = arg; 278df8bae1dSRodney W. Grimes new->c_func = ftn; 279df8bae1dSRodney W. Grimes 280df8bae1dSRodney W. Grimes /* 281df8bae1dSRodney W. Grimes * The time for each event is stored as a difference from the time 282df8bae1dSRodney W. Grimes * of the previous event on the queue. Walk the queue, correcting 283df8bae1dSRodney W. Grimes * the ticks argument for queue entries passed. Correct the ticks 284df8bae1dSRodney W. Grimes * value for the queue entry immediately after the insertion point 285df8bae1dSRodney W. Grimes * as well. Watch out for negative c_time values; these represent 286df8bae1dSRodney W. Grimes * overdue events. 287df8bae1dSRodney W. Grimes */ 288df8bae1dSRodney W. Grimes for (p = &calltodo; 289df8bae1dSRodney W. Grimes (t = p->c_next) != NULL && ticks > t->c_time; p = t) 290df8bae1dSRodney W. Grimes if (t->c_time > 0) 291df8bae1dSRodney W. Grimes ticks -= t->c_time; 292df8bae1dSRodney W. Grimes new->c_time = ticks; 293df8bae1dSRodney W. Grimes if (t != NULL) 294df8bae1dSRodney W. Grimes t->c_time -= ticks; 295df8bae1dSRodney W. Grimes 296df8bae1dSRodney W. Grimes /* Insert the new entry into the queue. */ 297df8bae1dSRodney W. Grimes p->c_next = new; 298df8bae1dSRodney W. Grimes new->c_next = t; 299df8bae1dSRodney W. Grimes splx(s); 300df8bae1dSRodney W. Grimes } 301df8bae1dSRodney W. Grimes 302df8bae1dSRodney W. Grimes void 303df8bae1dSRodney W. Grimes untimeout(ftn, arg) 304df8bae1dSRodney W. Grimes void (*ftn) __P((void *)); 305df8bae1dSRodney W. Grimes void *arg; 306df8bae1dSRodney W. Grimes { 307df8bae1dSRodney W. Grimes register struct callout *p, *t; 308df8bae1dSRodney W. Grimes register int s; 309df8bae1dSRodney W. Grimes 310df8bae1dSRodney W. Grimes s = splhigh(); 311df8bae1dSRodney W. Grimes for (p = &calltodo; (t = p->c_next) != NULL; p = t) 312df8bae1dSRodney W. Grimes if (t->c_func == ftn && t->c_arg == arg) { 313df8bae1dSRodney W. Grimes /* Increment next entry's tick count. */ 314df8bae1dSRodney W. Grimes if (t->c_next && t->c_time > 0) 315df8bae1dSRodney W. Grimes t->c_next->c_time += t->c_time; 316df8bae1dSRodney W. Grimes 317df8bae1dSRodney W. Grimes /* Move entry from callout queue to callfree queue. */ 318df8bae1dSRodney W. Grimes p->c_next = t->c_next; 319df8bae1dSRodney W. Grimes t->c_next = callfree; 320df8bae1dSRodney W. Grimes callfree = t; 321df8bae1dSRodney W. Grimes break; 322df8bae1dSRodney W. Grimes } 323df8bae1dSRodney W. Grimes splx(s); 324df8bae1dSRodney W. Grimes } 325df8bae1dSRodney W. Grimes 326df8bae1dSRodney W. Grimes /* 327df8bae1dSRodney W. Grimes * Compute number of hz until specified time. Used to 328df8bae1dSRodney W. Grimes * compute third argument to timeout() from an absolute time. 329df8bae1dSRodney W. Grimes */ 330df8bae1dSRodney W. Grimes int 331df8bae1dSRodney W. Grimes hzto(tv) 332df8bae1dSRodney W. Grimes struct timeval *tv; 333df8bae1dSRodney W. Grimes { 334df8bae1dSRodney W. Grimes register long ticks, sec; 335df8bae1dSRodney W. Grimes int s; 336df8bae1dSRodney W. Grimes 337df8bae1dSRodney W. Grimes /* 338df8bae1dSRodney W. Grimes * If number of milliseconds will fit in 32 bit arithmetic, 339df8bae1dSRodney W. Grimes * then compute number of milliseconds to time and scale to 340df8bae1dSRodney W. Grimes * ticks. Otherwise just compute number of hz in time, rounding 341df8bae1dSRodney W. Grimes * times greater than representible to maximum value. 342df8bae1dSRodney W. Grimes * 343df8bae1dSRodney W. Grimes * Delta times less than 25 days can be computed ``exactly''. 344df8bae1dSRodney W. Grimes * Maximum value for any timeout in 10ms ticks is 250 days. 345df8bae1dSRodney W. Grimes */ 346df8bae1dSRodney W. Grimes s = splhigh(); 347df8bae1dSRodney W. Grimes sec = tv->tv_sec - time.tv_sec; 348df8bae1dSRodney W. Grimes if (sec <= 0x7fffffff / 1000 - 1000) 349df8bae1dSRodney W. Grimes ticks = ((tv->tv_sec - time.tv_sec) * 1000 + 350df8bae1dSRodney W. Grimes (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000); 351df8bae1dSRodney W. Grimes else if (sec <= 0x7fffffff / hz) 352df8bae1dSRodney W. Grimes ticks = sec * hz; 353df8bae1dSRodney W. Grimes else 354df8bae1dSRodney W. Grimes ticks = 0x7fffffff; 355df8bae1dSRodney W. Grimes splx(s); 356df8bae1dSRodney W. Grimes return (ticks); 357df8bae1dSRodney W. Grimes } 358df8bae1dSRodney W. Grimes 359df8bae1dSRodney W. Grimes /* 360df8bae1dSRodney W. Grimes * Start profiling on a process. 361df8bae1dSRodney W. Grimes * 362df8bae1dSRodney W. Grimes * Kernel profiling passes proc0 which never exits and hence 363df8bae1dSRodney W. Grimes * keeps the profile clock running constantly. 364df8bae1dSRodney W. Grimes */ 365df8bae1dSRodney W. Grimes void 366df8bae1dSRodney W. Grimes startprofclock(p) 367df8bae1dSRodney W. Grimes register struct proc *p; 368df8bae1dSRodney W. Grimes { 369df8bae1dSRodney W. Grimes int s; 370df8bae1dSRodney W. Grimes 371df8bae1dSRodney W. Grimes if ((p->p_flag & P_PROFIL) == 0) { 372df8bae1dSRodney W. Grimes p->p_flag |= P_PROFIL; 373df8bae1dSRodney W. Grimes if (++profprocs == 1 && stathz != 0) { 374df8bae1dSRodney W. Grimes s = splstatclock(); 375df8bae1dSRodney W. Grimes psdiv = pscnt = psratio; 376df8bae1dSRodney W. Grimes setstatclockrate(profhz); 377df8bae1dSRodney W. Grimes splx(s); 378df8bae1dSRodney W. Grimes } 379df8bae1dSRodney W. Grimes } 380df8bae1dSRodney W. Grimes } 381df8bae1dSRodney W. Grimes 382df8bae1dSRodney W. Grimes /* 383df8bae1dSRodney W. Grimes * Stop profiling on a process. 384df8bae1dSRodney W. Grimes */ 385df8bae1dSRodney W. Grimes void 386df8bae1dSRodney W. Grimes stopprofclock(p) 387df8bae1dSRodney W. Grimes register struct proc *p; 388df8bae1dSRodney W. Grimes { 389df8bae1dSRodney W. Grimes int s; 390df8bae1dSRodney W. Grimes 391df8bae1dSRodney W. Grimes if (p->p_flag & P_PROFIL) { 392df8bae1dSRodney W. Grimes p->p_flag &= ~P_PROFIL; 393df8bae1dSRodney W. Grimes if (--profprocs == 0 && stathz != 0) { 394df8bae1dSRodney W. Grimes s = splstatclock(); 395df8bae1dSRodney W. Grimes psdiv = pscnt = 1; 396df8bae1dSRodney W. Grimes setstatclockrate(stathz); 397df8bae1dSRodney W. Grimes splx(s); 398df8bae1dSRodney W. Grimes } 399df8bae1dSRodney W. Grimes } 400df8bae1dSRodney W. Grimes } 401df8bae1dSRodney W. Grimes 402df8bae1dSRodney W. Grimes int dk_ndrive = DK_NDRIVE; 403df8bae1dSRodney W. Grimes 404df8bae1dSRodney W. Grimes /* 405df8bae1dSRodney W. Grimes * Statistics clock. Grab profile sample, and if divider reaches 0, 406df8bae1dSRodney W. Grimes * do process and kernel statistics. 407df8bae1dSRodney W. Grimes */ 408df8bae1dSRodney W. Grimes void 409df8bae1dSRodney W. Grimes statclock(frame) 410df8bae1dSRodney W. Grimes register struct clockframe *frame; 411df8bae1dSRodney W. Grimes { 412df8bae1dSRodney W. Grimes #ifdef GPROF 413df8bae1dSRodney W. Grimes register struct gmonparam *g; 414df8bae1dSRodney W. Grimes #endif 415df8bae1dSRodney W. Grimes register struct proc *p; 416df8bae1dSRodney W. Grimes register int i; 417df8bae1dSRodney W. Grimes 418df8bae1dSRodney W. Grimes if (CLKF_USERMODE(frame)) { 419df8bae1dSRodney W. Grimes p = curproc; 420df8bae1dSRodney W. Grimes if (p->p_flag & P_PROFIL) 421df8bae1dSRodney W. Grimes addupc_intr(p, CLKF_PC(frame), 1); 422df8bae1dSRodney W. Grimes if (--pscnt > 0) 423df8bae1dSRodney W. Grimes return; 424df8bae1dSRodney W. Grimes /* 425df8bae1dSRodney W. Grimes * Came from user mode; CPU was in user state. 426df8bae1dSRodney W. Grimes * If this process is being profiled record the tick. 427df8bae1dSRodney W. Grimes */ 428df8bae1dSRodney W. Grimes p->p_uticks++; 429df8bae1dSRodney W. Grimes if (p->p_nice > NZERO) 430df8bae1dSRodney W. Grimes cp_time[CP_NICE]++; 431df8bae1dSRodney W. Grimes else 432df8bae1dSRodney W. Grimes cp_time[CP_USER]++; 433df8bae1dSRodney W. Grimes } else { 434df8bae1dSRodney W. Grimes #ifdef GPROF 435df8bae1dSRodney W. Grimes /* 436df8bae1dSRodney W. Grimes * Kernel statistics are just like addupc_intr, only easier. 437df8bae1dSRodney W. Grimes */ 438df8bae1dSRodney W. Grimes g = &_gmonparam; 439df8bae1dSRodney W. Grimes if (g->state == GMON_PROF_ON) { 440df8bae1dSRodney W. Grimes i = CLKF_PC(frame) - g->lowpc; 441df8bae1dSRodney W. Grimes if (i < g->textsize) { 442df8bae1dSRodney W. Grimes i /= HISTFRACTION * sizeof(*g->kcount); 443df8bae1dSRodney W. Grimes g->kcount[i]++; 444df8bae1dSRodney W. Grimes } 445df8bae1dSRodney W. Grimes } 446df8bae1dSRodney W. Grimes #endif 447df8bae1dSRodney W. Grimes if (--pscnt > 0) 448df8bae1dSRodney W. Grimes return; 449df8bae1dSRodney W. Grimes /* 450df8bae1dSRodney W. Grimes * Came from kernel mode, so we were: 451df8bae1dSRodney W. Grimes * - handling an interrupt, 452df8bae1dSRodney W. Grimes * - doing syscall or trap work on behalf of the current 453df8bae1dSRodney W. Grimes * user process, or 454df8bae1dSRodney W. Grimes * - spinning in the idle loop. 455df8bae1dSRodney W. Grimes * Whichever it is, charge the time as appropriate. 456df8bae1dSRodney W. Grimes * Note that we charge interrupts to the current process, 457df8bae1dSRodney W. Grimes * regardless of whether they are ``for'' that process, 458df8bae1dSRodney W. Grimes * so that we know how much of its real time was spent 459df8bae1dSRodney W. Grimes * in ``non-process'' (i.e., interrupt) work. 460df8bae1dSRodney W. Grimes */ 461df8bae1dSRodney W. Grimes p = curproc; 462df8bae1dSRodney W. Grimes if (CLKF_INTR(frame)) { 463df8bae1dSRodney W. Grimes if (p != NULL) 464df8bae1dSRodney W. Grimes p->p_iticks++; 465df8bae1dSRodney W. Grimes cp_time[CP_INTR]++; 466df8bae1dSRodney W. Grimes } else if (p != NULL) { 467df8bae1dSRodney W. Grimes p->p_sticks++; 468df8bae1dSRodney W. Grimes cp_time[CP_SYS]++; 469df8bae1dSRodney W. Grimes } else 470df8bae1dSRodney W. Grimes cp_time[CP_IDLE]++; 471df8bae1dSRodney W. Grimes } 472df8bae1dSRodney W. Grimes pscnt = psdiv; 473df8bae1dSRodney W. Grimes 474df8bae1dSRodney W. Grimes /* 475df8bae1dSRodney W. Grimes * We maintain statistics shown by user-level statistics 476df8bae1dSRodney W. Grimes * programs: the amount of time in each cpu state, and 477df8bae1dSRodney W. Grimes * the amount of time each of DK_NDRIVE ``drives'' is busy. 478df8bae1dSRodney W. Grimes * 479df8bae1dSRodney W. Grimes * XXX should either run linked list of drives, or (better) 480df8bae1dSRodney W. Grimes * grab timestamps in the start & done code. 481df8bae1dSRodney W. Grimes */ 482df8bae1dSRodney W. Grimes for (i = 0; i < DK_NDRIVE; i++) 483df8bae1dSRodney W. Grimes if (dk_busy & (1 << i)) 484df8bae1dSRodney W. Grimes dk_time[i]++; 485df8bae1dSRodney W. Grimes 486df8bae1dSRodney W. Grimes /* 487df8bae1dSRodney W. Grimes * We adjust the priority of the current process. The priority of 488df8bae1dSRodney W. Grimes * a process gets worse as it accumulates CPU time. The cpu usage 489df8bae1dSRodney W. Grimes * estimator (p_estcpu) is increased here. The formula for computing 490df8bae1dSRodney W. Grimes * priorities (in kern_synch.c) will compute a different value each 491df8bae1dSRodney W. Grimes * time p_estcpu increases by 4. The cpu usage estimator ramps up 492df8bae1dSRodney W. Grimes * quite quickly when the process is running (linearly), and decays 493df8bae1dSRodney W. Grimes * away exponentially, at a rate which is proportionally slower when 494df8bae1dSRodney W. Grimes * the system is busy. The basic principal is that the system will 495df8bae1dSRodney W. Grimes * 90% forget that the process used a lot of CPU time in 5 * loadav 496df8bae1dSRodney W. Grimes * seconds. This causes the system to favor processes which haven't 497df8bae1dSRodney W. Grimes * run much recently, and to round-robin among other processes. 498df8bae1dSRodney W. Grimes */ 499df8bae1dSRodney W. Grimes if (p != NULL) { 500df8bae1dSRodney W. Grimes p->p_cpticks++; 501df8bae1dSRodney W. Grimes if (++p->p_estcpu == 0) 502df8bae1dSRodney W. Grimes p->p_estcpu--; 503df8bae1dSRodney W. Grimes if ((p->p_estcpu & 3) == 0) { 504df8bae1dSRodney W. Grimes resetpriority(p); 505df8bae1dSRodney W. Grimes if (p->p_priority >= PUSER) 506df8bae1dSRodney W. Grimes p->p_priority = p->p_usrpri; 507df8bae1dSRodney W. Grimes } 508df8bae1dSRodney W. Grimes } 509df8bae1dSRodney W. Grimes } 510df8bae1dSRodney W. Grimes 511df8bae1dSRodney W. Grimes /* 512df8bae1dSRodney W. Grimes * Return information about system clocks. 513df8bae1dSRodney W. Grimes */ 514df8bae1dSRodney W. Grimes sysctl_clockrate(where, sizep) 515df8bae1dSRodney W. Grimes register char *where; 516df8bae1dSRodney W. Grimes size_t *sizep; 517df8bae1dSRodney W. Grimes { 518df8bae1dSRodney W. Grimes struct clockinfo clkinfo; 519df8bae1dSRodney W. Grimes 520df8bae1dSRodney W. Grimes /* 521df8bae1dSRodney W. Grimes * Construct clockinfo structure. 522df8bae1dSRodney W. Grimes */ 523df8bae1dSRodney W. Grimes clkinfo.hz = hz; 524df8bae1dSRodney W. Grimes clkinfo.tick = tick; 525df8bae1dSRodney W. Grimes clkinfo.profhz = profhz; 526df8bae1dSRodney W. Grimes clkinfo.stathz = stathz ? stathz : hz; 527df8bae1dSRodney W. Grimes return (sysctl_rdstruct(where, sizep, NULL, &clkinfo, sizeof(clkinfo))); 528df8bae1dSRodney W. Grimes } 529