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 398a129caeSDavid Greenman * $Id: kern_clock.c,v 1.4 1994/08/18 22:34:58 wollman Exp $ 40df8bae1dSRodney W. Grimes */ 41df8bae1dSRodney W. Grimes 42df8bae1dSRodney W. Grimes #include <sys/param.h> 43df8bae1dSRodney W. Grimes #include <sys/systm.h> 44df8bae1dSRodney W. Grimes #include <sys/dkstat.h> 45df8bae1dSRodney W. Grimes #include <sys/callout.h> 46df8bae1dSRodney W. Grimes #include <sys/kernel.h> 47df8bae1dSRodney W. Grimes #include <sys/proc.h> 48df8bae1dSRodney W. Grimes #include <sys/resourcevar.h> 498a129caeSDavid Greenman #include <vm/vm.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes #include <machine/cpu.h> 52df8bae1dSRodney W. Grimes 53df8bae1dSRodney W. Grimes #ifdef GPROF 54df8bae1dSRodney W. Grimes #include <sys/gmon.h> 55df8bae1dSRodney W. Grimes #endif 56df8bae1dSRodney W. Grimes 57f23b4c91SGarrett Wollman /* Does anybody else really care about these? */ 58f23b4c91SGarrett Wollman struct callout *callfree, *callout, calltodo; 59f23b4c91SGarrett Wollman int ncallout; 60f23b4c91SGarrett Wollman 61f23b4c91SGarrett Wollman /* Some of these don't belong here, but it's easiest to concentrate them. */ 62f23b4c91SGarrett Wollman long cp_time[CPUSTATES]; 63f23b4c91SGarrett Wollman long dk_seek[DK_NDRIVE]; 64f23b4c91SGarrett Wollman long dk_time[DK_NDRIVE]; 65f23b4c91SGarrett Wollman long dk_wds[DK_NDRIVE]; 66f23b4c91SGarrett Wollman long dk_wpms[DK_NDRIVE]; 67f23b4c91SGarrett Wollman long dk_xfer[DK_NDRIVE]; 68f23b4c91SGarrett Wollman 69f23b4c91SGarrett Wollman int dk_busy; 70f23b4c91SGarrett Wollman int dk_ndrive = DK_NDRIVE; 71f23b4c91SGarrett Wollman 72f23b4c91SGarrett Wollman long tk_cancc; 73f23b4c91SGarrett Wollman long tk_nin; 74f23b4c91SGarrett Wollman long tk_nout; 75f23b4c91SGarrett Wollman long tk_rawcc; 76f23b4c91SGarrett Wollman 77df8bae1dSRodney W. Grimes /* 78df8bae1dSRodney W. Grimes * Clock handling routines. 79df8bae1dSRodney W. Grimes * 80df8bae1dSRodney W. Grimes * This code is written to operate with two timers that run independently of 81df8bae1dSRodney W. Grimes * each other. The main clock, running hz times per second, is used to keep 82df8bae1dSRodney W. Grimes * track of real time. The second timer handles kernel and user profiling, 83df8bae1dSRodney W. Grimes * and does resource use estimation. If the second timer is programmable, 84df8bae1dSRodney W. Grimes * it is randomized to avoid aliasing between the two clocks. For example, 85df8bae1dSRodney W. Grimes * the randomization prevents an adversary from always giving up the cpu 86df8bae1dSRodney W. Grimes * just before its quantum expires. Otherwise, it would never accumulate 87df8bae1dSRodney W. Grimes * cpu ticks. The mean frequency of the second timer is stathz. 88df8bae1dSRodney W. Grimes * 89df8bae1dSRodney W. Grimes * If no second timer exists, stathz will be zero; in this case we drive 90df8bae1dSRodney W. Grimes * profiling and statistics off the main clock. This WILL NOT be accurate; 91df8bae1dSRodney W. Grimes * do not do it unless absolutely necessary. 92df8bae1dSRodney W. Grimes * 93df8bae1dSRodney W. Grimes * The statistics clock may (or may not) be run at a higher rate while 94df8bae1dSRodney W. Grimes * profiling. This profile clock runs at profhz. We require that profhz 95df8bae1dSRodney W. Grimes * be an integral multiple of stathz. 96df8bae1dSRodney W. Grimes * 97df8bae1dSRodney W. Grimes * If the statistics clock is running fast, it must be divided by the ratio 98df8bae1dSRodney W. Grimes * profhz/stathz for statistics. (For profiling, every tick counts.) 99df8bae1dSRodney W. Grimes */ 100df8bae1dSRodney W. Grimes 101df8bae1dSRodney W. Grimes /* 102df8bae1dSRodney W. Grimes * TODO: 103df8bae1dSRodney W. Grimes * allocate more timeout table slots when table overflows. 104df8bae1dSRodney W. Grimes */ 105df8bae1dSRodney W. Grimes 106df8bae1dSRodney W. Grimes /* 107df8bae1dSRodney W. Grimes * Bump a timeval by a small number of usec's. 108df8bae1dSRodney W. Grimes */ 109df8bae1dSRodney W. Grimes #define BUMPTIME(t, usec) { \ 110df8bae1dSRodney W. Grimes register volatile struct timeval *tp = (t); \ 111df8bae1dSRodney W. Grimes register long us; \ 112df8bae1dSRodney W. Grimes \ 113df8bae1dSRodney W. Grimes tp->tv_usec = us = tp->tv_usec + (usec); \ 114df8bae1dSRodney W. Grimes if (us >= 1000000) { \ 115df8bae1dSRodney W. Grimes tp->tv_usec = us - 1000000; \ 116df8bae1dSRodney W. Grimes tp->tv_sec++; \ 117df8bae1dSRodney W. Grimes } \ 118df8bae1dSRodney W. Grimes } 119df8bae1dSRodney W. Grimes 120df8bae1dSRodney W. Grimes int stathz; 121df8bae1dSRodney W. Grimes int profhz; 122df8bae1dSRodney W. Grimes int profprocs; 123df8bae1dSRodney W. Grimes int ticks; 124df8bae1dSRodney W. Grimes static int psdiv, pscnt; /* prof => stat divider */ 125df8bae1dSRodney W. Grimes int psratio; /* ratio: prof / stat */ 126df8bae1dSRodney W. Grimes 127df8bae1dSRodney W. Grimes volatile struct timeval time; 128df8bae1dSRodney W. Grimes volatile struct timeval mono_time; 129df8bae1dSRodney W. Grimes 130df8bae1dSRodney W. Grimes /* 131df8bae1dSRodney W. Grimes * Initialize clock frequencies and start both clocks running. 132df8bae1dSRodney W. Grimes */ 133df8bae1dSRodney W. Grimes void 134df8bae1dSRodney W. Grimes initclocks() 135df8bae1dSRodney W. Grimes { 136df8bae1dSRodney W. Grimes register int i; 137df8bae1dSRodney W. Grimes 138df8bae1dSRodney W. Grimes /* 139df8bae1dSRodney W. Grimes * Set divisors to 1 (normal case) and let the machine-specific 140df8bae1dSRodney W. Grimes * code do its bit. 141df8bae1dSRodney W. Grimes */ 142df8bae1dSRodney W. Grimes psdiv = pscnt = 1; 143df8bae1dSRodney W. Grimes cpu_initclocks(); 144df8bae1dSRodney W. Grimes 145df8bae1dSRodney W. Grimes /* 146df8bae1dSRodney W. Grimes * Compute profhz/stathz, and fix profhz if needed. 147df8bae1dSRodney W. Grimes */ 148df8bae1dSRodney W. Grimes i = stathz ? stathz : hz; 149df8bae1dSRodney W. Grimes if (profhz == 0) 150df8bae1dSRodney W. Grimes profhz = i; 151df8bae1dSRodney W. Grimes psratio = profhz / i; 152df8bae1dSRodney W. Grimes } 153df8bae1dSRodney W. Grimes 154df8bae1dSRodney W. Grimes /* 155df8bae1dSRodney W. Grimes * The real-time timer, interrupting hz times per second. 156df8bae1dSRodney W. Grimes */ 157df8bae1dSRodney W. Grimes void 158df8bae1dSRodney W. Grimes hardclock(frame) 159df8bae1dSRodney W. Grimes register struct clockframe *frame; 160df8bae1dSRodney W. Grimes { 161df8bae1dSRodney W. Grimes register struct callout *p1; 162df8bae1dSRodney W. Grimes register struct proc *p; 163df8bae1dSRodney W. Grimes register int delta, needsoft; 164df8bae1dSRodney W. Grimes extern int tickdelta; 165df8bae1dSRodney W. Grimes extern long timedelta; 166df8bae1dSRodney W. Grimes 167df8bae1dSRodney W. Grimes /* 168df8bae1dSRodney W. Grimes * Update real-time timeout queue. 169df8bae1dSRodney W. Grimes * At front of queue are some number of events which are ``due''. 170df8bae1dSRodney W. Grimes * The time to these is <= 0 and if negative represents the 171df8bae1dSRodney W. Grimes * number of ticks which have passed since it was supposed to happen. 172df8bae1dSRodney W. Grimes * The rest of the q elements (times > 0) are events yet to happen, 173df8bae1dSRodney W. Grimes * where the time for each is given as a delta from the previous. 174df8bae1dSRodney W. Grimes * Decrementing just the first of these serves to decrement the time 175df8bae1dSRodney W. Grimes * to all events. 176df8bae1dSRodney W. Grimes */ 177df8bae1dSRodney W. Grimes needsoft = 0; 178df8bae1dSRodney W. Grimes for (p1 = calltodo.c_next; p1 != NULL; p1 = p1->c_next) { 179df8bae1dSRodney W. Grimes if (--p1->c_time > 0) 180df8bae1dSRodney W. Grimes break; 181df8bae1dSRodney W. Grimes needsoft = 1; 182df8bae1dSRodney W. Grimes if (p1->c_time == 0) 183df8bae1dSRodney W. Grimes break; 184df8bae1dSRodney W. Grimes } 185df8bae1dSRodney W. Grimes 186df8bae1dSRodney W. Grimes p = curproc; 187df8bae1dSRodney W. Grimes if (p) { 188df8bae1dSRodney W. Grimes register struct pstats *pstats; 189df8bae1dSRodney W. Grimes 190df8bae1dSRodney W. Grimes /* 191df8bae1dSRodney W. Grimes * Run current process's virtual and profile time, as needed. 192df8bae1dSRodney W. Grimes */ 193df8bae1dSRodney W. Grimes pstats = p->p_stats; 194df8bae1dSRodney W. Grimes if (CLKF_USERMODE(frame) && 195df8bae1dSRodney W. Grimes timerisset(&pstats->p_timer[ITIMER_VIRTUAL].it_value) && 196df8bae1dSRodney W. Grimes itimerdecr(&pstats->p_timer[ITIMER_VIRTUAL], tick) == 0) 197df8bae1dSRodney W. Grimes psignal(p, SIGVTALRM); 198df8bae1dSRodney W. Grimes if (timerisset(&pstats->p_timer[ITIMER_PROF].it_value) && 199df8bae1dSRodney W. Grimes itimerdecr(&pstats->p_timer[ITIMER_PROF], tick) == 0) 200df8bae1dSRodney W. Grimes psignal(p, SIGPROF); 201df8bae1dSRodney W. Grimes } 202df8bae1dSRodney W. Grimes 203df8bae1dSRodney W. Grimes /* 204df8bae1dSRodney W. Grimes * If no separate statistics clock is available, run it from here. 205df8bae1dSRodney W. Grimes */ 206df8bae1dSRodney W. Grimes if (stathz == 0) 207df8bae1dSRodney W. Grimes statclock(frame); 208df8bae1dSRodney W. Grimes 209df8bae1dSRodney W. Grimes /* 210df8bae1dSRodney W. Grimes * Increment the time-of-day. The increment is just ``tick'' unless 211df8bae1dSRodney W. Grimes * we are still adjusting the clock; see adjtime(). 212df8bae1dSRodney W. Grimes */ 213df8bae1dSRodney W. Grimes ticks++; 214df8bae1dSRodney W. Grimes if (timedelta == 0) 215df8bae1dSRodney W. Grimes delta = tick; 216df8bae1dSRodney W. Grimes else { 217df8bae1dSRodney W. Grimes delta = tick + tickdelta; 218df8bae1dSRodney W. Grimes timedelta -= tickdelta; 219df8bae1dSRodney W. Grimes } 220df8bae1dSRodney W. Grimes BUMPTIME(&time, delta); 221df8bae1dSRodney W. Grimes BUMPTIME(&mono_time, delta); 222df8bae1dSRodney W. Grimes 223df8bae1dSRodney W. Grimes /* 224df8bae1dSRodney W. Grimes * Process callouts at a very low cpu priority, so we don't keep the 225df8bae1dSRodney W. Grimes * relatively high clock interrupt priority any longer than necessary. 226df8bae1dSRodney W. Grimes */ 227df8bae1dSRodney W. Grimes if (needsoft) { 228df8bae1dSRodney W. Grimes if (CLKF_BASEPRI(frame)) { 229df8bae1dSRodney W. Grimes /* 230df8bae1dSRodney W. Grimes * Save the overhead of a software interrupt; 231df8bae1dSRodney W. Grimes * it will happen as soon as we return, so do it now. 232df8bae1dSRodney W. Grimes */ 233df8bae1dSRodney W. Grimes (void)splsoftclock(); 234df8bae1dSRodney W. Grimes softclock(); 235df8bae1dSRodney W. Grimes } else 236df8bae1dSRodney W. Grimes setsoftclock(); 237df8bae1dSRodney W. Grimes } 238df8bae1dSRodney W. Grimes } 239df8bae1dSRodney W. Grimes 240df8bae1dSRodney W. Grimes /* 241df8bae1dSRodney W. Grimes * Software (low priority) clock interrupt. 242df8bae1dSRodney W. Grimes * Run periodic events from timeout queue. 243df8bae1dSRodney W. Grimes */ 244df8bae1dSRodney W. Grimes /*ARGSUSED*/ 245df8bae1dSRodney W. Grimes void 246df8bae1dSRodney W. Grimes softclock() 247df8bae1dSRodney W. Grimes { 248df8bae1dSRodney W. Grimes register struct callout *c; 249df8bae1dSRodney W. Grimes register void *arg; 250df8bae1dSRodney W. Grimes register void (*func) __P((void *)); 251df8bae1dSRodney W. Grimes register int s; 252df8bae1dSRodney W. Grimes 253df8bae1dSRodney W. Grimes s = splhigh(); 254df8bae1dSRodney W. Grimes while ((c = calltodo.c_next) != NULL && c->c_time <= 0) { 255df8bae1dSRodney W. Grimes func = c->c_func; 256df8bae1dSRodney W. Grimes arg = c->c_arg; 257df8bae1dSRodney W. Grimes calltodo.c_next = c->c_next; 258df8bae1dSRodney W. Grimes c->c_next = callfree; 259df8bae1dSRodney W. Grimes callfree = c; 260df8bae1dSRodney W. Grimes splx(s); 261df8bae1dSRodney W. Grimes (*func)(arg); 262df8bae1dSRodney W. Grimes (void) splhigh(); 263df8bae1dSRodney W. Grimes } 264df8bae1dSRodney W. Grimes splx(s); 265df8bae1dSRodney W. Grimes } 266df8bae1dSRodney W. Grimes 267df8bae1dSRodney W. Grimes /* 268df8bae1dSRodney W. Grimes * timeout -- 269df8bae1dSRodney W. Grimes * Execute a function after a specified length of time. 270df8bae1dSRodney W. Grimes * 271df8bae1dSRodney W. Grimes * untimeout -- 272df8bae1dSRodney W. Grimes * Cancel previous timeout function call. 273df8bae1dSRodney W. Grimes * 274df8bae1dSRodney W. Grimes * See AT&T BCI Driver Reference Manual for specification. This 275df8bae1dSRodney W. Grimes * implementation differs from that one in that no identification 276df8bae1dSRodney W. Grimes * value is returned from timeout, rather, the original arguments 277df8bae1dSRodney W. Grimes * to timeout are used to identify entries for untimeout. 278df8bae1dSRodney W. Grimes */ 279df8bae1dSRodney W. Grimes void 280df8bae1dSRodney W. Grimes timeout(ftn, arg, ticks) 281f23b4c91SGarrett Wollman timeout_t ftn; 282df8bae1dSRodney W. Grimes void *arg; 283df8bae1dSRodney W. Grimes register int ticks; 284df8bae1dSRodney W. Grimes { 285df8bae1dSRodney W. Grimes register struct callout *new, *p, *t; 286df8bae1dSRodney W. Grimes register int s; 287df8bae1dSRodney W. Grimes 288df8bae1dSRodney W. Grimes if (ticks <= 0) 289df8bae1dSRodney W. Grimes ticks = 1; 290df8bae1dSRodney W. Grimes 291df8bae1dSRodney W. Grimes /* Lock out the clock. */ 292df8bae1dSRodney W. Grimes s = splhigh(); 293df8bae1dSRodney W. Grimes 294df8bae1dSRodney W. Grimes /* Fill in the next free callout structure. */ 295df8bae1dSRodney W. Grimes if (callfree == NULL) 296df8bae1dSRodney W. Grimes panic("timeout table full"); 297df8bae1dSRodney W. Grimes new = callfree; 298df8bae1dSRodney W. Grimes callfree = new->c_next; 299df8bae1dSRodney W. Grimes new->c_arg = arg; 300df8bae1dSRodney W. Grimes new->c_func = ftn; 301df8bae1dSRodney W. Grimes 302df8bae1dSRodney W. Grimes /* 303df8bae1dSRodney W. Grimes * The time for each event is stored as a difference from the time 304df8bae1dSRodney W. Grimes * of the previous event on the queue. Walk the queue, correcting 305df8bae1dSRodney W. Grimes * the ticks argument for queue entries passed. Correct the ticks 306df8bae1dSRodney W. Grimes * value for the queue entry immediately after the insertion point 307df8bae1dSRodney W. Grimes * as well. Watch out for negative c_time values; these represent 308df8bae1dSRodney W. Grimes * overdue events. 309df8bae1dSRodney W. Grimes */ 310df8bae1dSRodney W. Grimes for (p = &calltodo; 311df8bae1dSRodney W. Grimes (t = p->c_next) != NULL && ticks > t->c_time; p = t) 312df8bae1dSRodney W. Grimes if (t->c_time > 0) 313df8bae1dSRodney W. Grimes ticks -= t->c_time; 314df8bae1dSRodney W. Grimes new->c_time = ticks; 315df8bae1dSRodney W. Grimes if (t != NULL) 316df8bae1dSRodney W. Grimes t->c_time -= ticks; 317df8bae1dSRodney W. Grimes 318df8bae1dSRodney W. Grimes /* Insert the new entry into the queue. */ 319df8bae1dSRodney W. Grimes p->c_next = new; 320df8bae1dSRodney W. Grimes new->c_next = t; 321df8bae1dSRodney W. Grimes splx(s); 322df8bae1dSRodney W. Grimes } 323df8bae1dSRodney W. Grimes 324df8bae1dSRodney W. Grimes void 325df8bae1dSRodney W. Grimes untimeout(ftn, arg) 326f23b4c91SGarrett Wollman timeout_t ftn; 327df8bae1dSRodney W. Grimes void *arg; 328df8bae1dSRodney W. Grimes { 329df8bae1dSRodney W. Grimes register struct callout *p, *t; 330df8bae1dSRodney W. Grimes register int s; 331df8bae1dSRodney W. Grimes 332df8bae1dSRodney W. Grimes s = splhigh(); 333df8bae1dSRodney W. Grimes for (p = &calltodo; (t = p->c_next) != NULL; p = t) 334df8bae1dSRodney W. Grimes if (t->c_func == ftn && t->c_arg == arg) { 335df8bae1dSRodney W. Grimes /* Increment next entry's tick count. */ 336df8bae1dSRodney W. Grimes if (t->c_next && t->c_time > 0) 337df8bae1dSRodney W. Grimes t->c_next->c_time += t->c_time; 338df8bae1dSRodney W. Grimes 339df8bae1dSRodney W. Grimes /* Move entry from callout queue to callfree queue. */ 340df8bae1dSRodney W. Grimes p->c_next = t->c_next; 341df8bae1dSRodney W. Grimes t->c_next = callfree; 342df8bae1dSRodney W. Grimes callfree = t; 343df8bae1dSRodney W. Grimes break; 344df8bae1dSRodney W. Grimes } 345df8bae1dSRodney W. Grimes splx(s); 346df8bae1dSRodney W. Grimes } 347df8bae1dSRodney W. Grimes 348df8bae1dSRodney W. Grimes /* 349df8bae1dSRodney W. Grimes * Compute number of hz until specified time. Used to 350df8bae1dSRodney W. Grimes * compute third argument to timeout() from an absolute time. 351df8bae1dSRodney W. Grimes */ 352df8bae1dSRodney W. Grimes int 353df8bae1dSRodney W. Grimes hzto(tv) 354df8bae1dSRodney W. Grimes struct timeval *tv; 355df8bae1dSRodney W. Grimes { 356df8bae1dSRodney W. Grimes register long ticks, sec; 357df8bae1dSRodney W. Grimes int s; 358df8bae1dSRodney W. Grimes 359df8bae1dSRodney W. Grimes /* 360df8bae1dSRodney W. Grimes * If number of milliseconds will fit in 32 bit arithmetic, 361df8bae1dSRodney W. Grimes * then compute number of milliseconds to time and scale to 362df8bae1dSRodney W. Grimes * ticks. Otherwise just compute number of hz in time, rounding 363df8bae1dSRodney W. Grimes * times greater than representible to maximum value. 364df8bae1dSRodney W. Grimes * 365df8bae1dSRodney W. Grimes * Delta times less than 25 days can be computed ``exactly''. 366df8bae1dSRodney W. Grimes * Maximum value for any timeout in 10ms ticks is 250 days. 367df8bae1dSRodney W. Grimes */ 368df8bae1dSRodney W. Grimes s = splhigh(); 369df8bae1dSRodney W. Grimes sec = tv->tv_sec - time.tv_sec; 370df8bae1dSRodney W. Grimes if (sec <= 0x7fffffff / 1000 - 1000) 371df8bae1dSRodney W. Grimes ticks = ((tv->tv_sec - time.tv_sec) * 1000 + 372df8bae1dSRodney W. Grimes (tv->tv_usec - time.tv_usec) / 1000) / (tick / 1000); 373df8bae1dSRodney W. Grimes else if (sec <= 0x7fffffff / hz) 374df8bae1dSRodney W. Grimes ticks = sec * hz; 375df8bae1dSRodney W. Grimes else 376df8bae1dSRodney W. Grimes ticks = 0x7fffffff; 377df8bae1dSRodney W. Grimes splx(s); 378df8bae1dSRodney W. Grimes return (ticks); 379df8bae1dSRodney W. Grimes } 380df8bae1dSRodney W. Grimes 381df8bae1dSRodney W. Grimes /* 382df8bae1dSRodney W. Grimes * Start profiling on a process. 383df8bae1dSRodney W. Grimes * 384df8bae1dSRodney W. Grimes * Kernel profiling passes proc0 which never exits and hence 385df8bae1dSRodney W. Grimes * keeps the profile clock running constantly. 386df8bae1dSRodney W. Grimes */ 387df8bae1dSRodney W. Grimes void 388df8bae1dSRodney W. Grimes startprofclock(p) 389df8bae1dSRodney W. Grimes register struct proc *p; 390df8bae1dSRodney W. Grimes { 391df8bae1dSRodney W. Grimes int s; 392df8bae1dSRodney W. Grimes 393df8bae1dSRodney W. Grimes if ((p->p_flag & P_PROFIL) == 0) { 394df8bae1dSRodney W. Grimes p->p_flag |= P_PROFIL; 395df8bae1dSRodney W. Grimes if (++profprocs == 1 && stathz != 0) { 396df8bae1dSRodney W. Grimes s = splstatclock(); 397df8bae1dSRodney W. Grimes psdiv = pscnt = psratio; 398df8bae1dSRodney W. Grimes setstatclockrate(profhz); 399df8bae1dSRodney W. Grimes splx(s); 400df8bae1dSRodney W. Grimes } 401df8bae1dSRodney W. Grimes } 402df8bae1dSRodney W. Grimes } 403df8bae1dSRodney W. Grimes 404df8bae1dSRodney W. Grimes /* 405df8bae1dSRodney W. Grimes * Stop profiling on a process. 406df8bae1dSRodney W. Grimes */ 407df8bae1dSRodney W. Grimes void 408df8bae1dSRodney W. Grimes stopprofclock(p) 409df8bae1dSRodney W. Grimes register struct proc *p; 410df8bae1dSRodney W. Grimes { 411df8bae1dSRodney W. Grimes int s; 412df8bae1dSRodney W. Grimes 413df8bae1dSRodney W. Grimes if (p->p_flag & P_PROFIL) { 414df8bae1dSRodney W. Grimes p->p_flag &= ~P_PROFIL; 415df8bae1dSRodney W. Grimes if (--profprocs == 0 && stathz != 0) { 416df8bae1dSRodney W. Grimes s = splstatclock(); 417df8bae1dSRodney W. Grimes psdiv = pscnt = 1; 418df8bae1dSRodney W. Grimes setstatclockrate(stathz); 419df8bae1dSRodney W. Grimes splx(s); 420df8bae1dSRodney W. Grimes } 421df8bae1dSRodney W. Grimes } 422df8bae1dSRodney W. Grimes } 423df8bae1dSRodney W. Grimes 424df8bae1dSRodney W. Grimes /* 425df8bae1dSRodney W. Grimes * Statistics clock. Grab profile sample, and if divider reaches 0, 426df8bae1dSRodney W. Grimes * do process and kernel statistics. 427df8bae1dSRodney W. Grimes */ 428df8bae1dSRodney W. Grimes void 429df8bae1dSRodney W. Grimes statclock(frame) 430df8bae1dSRodney W. Grimes register struct clockframe *frame; 431df8bae1dSRodney W. Grimes { 432df8bae1dSRodney W. Grimes #ifdef GPROF 433df8bae1dSRodney W. Grimes register struct gmonparam *g; 434df8bae1dSRodney W. Grimes #endif 4358a129caeSDavid Greenman register struct proc *p = curproc; 436df8bae1dSRodney W. Grimes register int i; 437df8bae1dSRodney W. Grimes 4388a129caeSDavid Greenman if (p) { 4398a129caeSDavid Greenman struct pstats *pstats; 4408a129caeSDavid Greenman struct rusage *ru; 4418a129caeSDavid Greenman struct vmspace *vm; 4428a129caeSDavid Greenman 4438a129caeSDavid Greenman /* bump the resource usage of integral space use */ 4448a129caeSDavid Greenman if ((pstats = p->p_stats) && (ru = &pstats->p_ru) && (vm = p->p_vmspace)) { 4458a129caeSDavid Greenman ru->ru_ixrss += vm->vm_tsize * PAGE_SIZE / 1024; 4468a129caeSDavid Greenman ru->ru_idrss += vm->vm_dsize * PAGE_SIZE / 1024; 4478a129caeSDavid Greenman ru->ru_isrss += vm->vm_ssize * PAGE_SIZE / 1024; 4488a129caeSDavid Greenman if ((vm->vm_pmap.pm_stats.resident_count * PAGE_SIZE / 1024) > 4498a129caeSDavid Greenman ru->ru_maxrss) { 4508a129caeSDavid Greenman ru->ru_maxrss = 4518a129caeSDavid Greenman vm->vm_pmap.pm_stats.resident_count * PAGE_SIZE / 1024; 4528a129caeSDavid Greenman } 4538a129caeSDavid Greenman } 4548a129caeSDavid Greenman } 4558a129caeSDavid Greenman 456df8bae1dSRodney W. Grimes if (CLKF_USERMODE(frame)) { 457df8bae1dSRodney W. Grimes if (p->p_flag & P_PROFIL) 458df8bae1dSRodney W. Grimes addupc_intr(p, CLKF_PC(frame), 1); 459df8bae1dSRodney W. Grimes if (--pscnt > 0) 460df8bae1dSRodney W. Grimes return; 461df8bae1dSRodney W. Grimes /* 462df8bae1dSRodney W. Grimes * Came from user mode; CPU was in user state. 463df8bae1dSRodney W. Grimes * If this process is being profiled record the tick. 464df8bae1dSRodney W. Grimes */ 465df8bae1dSRodney W. Grimes p->p_uticks++; 466df8bae1dSRodney W. Grimes if (p->p_nice > NZERO) 467df8bae1dSRodney W. Grimes cp_time[CP_NICE]++; 468df8bae1dSRodney W. Grimes else 469df8bae1dSRodney W. Grimes cp_time[CP_USER]++; 470df8bae1dSRodney W. Grimes } else { 471df8bae1dSRodney W. Grimes #ifdef GPROF 472df8bae1dSRodney W. Grimes /* 473df8bae1dSRodney W. Grimes * Kernel statistics are just like addupc_intr, only easier. 474df8bae1dSRodney W. Grimes */ 475df8bae1dSRodney W. Grimes g = &_gmonparam; 476df8bae1dSRodney W. Grimes if (g->state == GMON_PROF_ON) { 477df8bae1dSRodney W. Grimes i = CLKF_PC(frame) - g->lowpc; 478df8bae1dSRodney W. Grimes if (i < g->textsize) { 479df8bae1dSRodney W. Grimes i /= HISTFRACTION * sizeof(*g->kcount); 480df8bae1dSRodney W. Grimes g->kcount[i]++; 481df8bae1dSRodney W. Grimes } 482df8bae1dSRodney W. Grimes } 483df8bae1dSRodney W. Grimes #endif 484df8bae1dSRodney W. Grimes if (--pscnt > 0) 485df8bae1dSRodney W. Grimes return; 486df8bae1dSRodney W. Grimes /* 487df8bae1dSRodney W. Grimes * Came from kernel mode, so we were: 488df8bae1dSRodney W. Grimes * - handling an interrupt, 489df8bae1dSRodney W. Grimes * - doing syscall or trap work on behalf of the current 490df8bae1dSRodney W. Grimes * user process, or 491df8bae1dSRodney W. Grimes * - spinning in the idle loop. 492df8bae1dSRodney W. Grimes * Whichever it is, charge the time as appropriate. 493df8bae1dSRodney W. Grimes * Note that we charge interrupts to the current process, 494df8bae1dSRodney W. Grimes * regardless of whether they are ``for'' that process, 495df8bae1dSRodney W. Grimes * so that we know how much of its real time was spent 496df8bae1dSRodney W. Grimes * in ``non-process'' (i.e., interrupt) work. 497df8bae1dSRodney W. Grimes */ 498df8bae1dSRodney W. Grimes if (CLKF_INTR(frame)) { 499df8bae1dSRodney W. Grimes if (p != NULL) 500df8bae1dSRodney W. Grimes p->p_iticks++; 501df8bae1dSRodney W. Grimes cp_time[CP_INTR]++; 502df8bae1dSRodney W. Grimes } else if (p != NULL) { 503df8bae1dSRodney W. Grimes p->p_sticks++; 504df8bae1dSRodney W. Grimes cp_time[CP_SYS]++; 505df8bae1dSRodney W. Grimes } else 506df8bae1dSRodney W. Grimes cp_time[CP_IDLE]++; 507df8bae1dSRodney W. Grimes } 508df8bae1dSRodney W. Grimes pscnt = psdiv; 509df8bae1dSRodney W. Grimes 510df8bae1dSRodney W. Grimes /* 511df8bae1dSRodney W. Grimes * We maintain statistics shown by user-level statistics 512df8bae1dSRodney W. Grimes * programs: the amount of time in each cpu state, and 513df8bae1dSRodney W. Grimes * the amount of time each of DK_NDRIVE ``drives'' is busy. 514df8bae1dSRodney W. Grimes * 515df8bae1dSRodney W. Grimes * XXX should either run linked list of drives, or (better) 516df8bae1dSRodney W. Grimes * grab timestamps in the start & done code. 517df8bae1dSRodney W. Grimes */ 518df8bae1dSRodney W. Grimes for (i = 0; i < DK_NDRIVE; i++) 519df8bae1dSRodney W. Grimes if (dk_busy & (1 << i)) 520df8bae1dSRodney W. Grimes dk_time[i]++; 521df8bae1dSRodney W. Grimes 522df8bae1dSRodney W. Grimes /* 523df8bae1dSRodney W. Grimes * We adjust the priority of the current process. The priority of 524df8bae1dSRodney W. Grimes * a process gets worse as it accumulates CPU time. The cpu usage 525df8bae1dSRodney W. Grimes * estimator (p_estcpu) is increased here. The formula for computing 526df8bae1dSRodney W. Grimes * priorities (in kern_synch.c) will compute a different value each 527df8bae1dSRodney W. Grimes * time p_estcpu increases by 4. The cpu usage estimator ramps up 528df8bae1dSRodney W. Grimes * quite quickly when the process is running (linearly), and decays 529df8bae1dSRodney W. Grimes * away exponentially, at a rate which is proportionally slower when 530df8bae1dSRodney W. Grimes * the system is busy. The basic principal is that the system will 531df8bae1dSRodney W. Grimes * 90% forget that the process used a lot of CPU time in 5 * loadav 532df8bae1dSRodney W. Grimes * seconds. This causes the system to favor processes which haven't 533df8bae1dSRodney W. Grimes * run much recently, and to round-robin among other processes. 534df8bae1dSRodney W. Grimes */ 535df8bae1dSRodney W. Grimes if (p != NULL) { 536df8bae1dSRodney W. Grimes p->p_cpticks++; 537df8bae1dSRodney W. Grimes if (++p->p_estcpu == 0) 538df8bae1dSRodney W. Grimes p->p_estcpu--; 539df8bae1dSRodney W. Grimes if ((p->p_estcpu & 3) == 0) { 540df8bae1dSRodney W. Grimes resetpriority(p); 541df8bae1dSRodney W. Grimes if (p->p_priority >= PUSER) 542df8bae1dSRodney W. Grimes p->p_priority = p->p_usrpri; 543df8bae1dSRodney W. Grimes } 544df8bae1dSRodney W. Grimes } 545df8bae1dSRodney W. Grimes } 546df8bae1dSRodney W. Grimes 547df8bae1dSRodney W. Grimes /* 548df8bae1dSRodney W. Grimes * Return information about system clocks. 549df8bae1dSRodney W. Grimes */ 55026f9a767SRodney W. Grimes int 551df8bae1dSRodney W. Grimes sysctl_clockrate(where, sizep) 552df8bae1dSRodney W. Grimes register char *where; 553df8bae1dSRodney W. Grimes size_t *sizep; 554df8bae1dSRodney W. Grimes { 555df8bae1dSRodney W. Grimes struct clockinfo clkinfo; 556df8bae1dSRodney W. Grimes 557df8bae1dSRodney W. Grimes /* 558df8bae1dSRodney W. Grimes * Construct clockinfo structure. 559df8bae1dSRodney W. Grimes */ 560df8bae1dSRodney W. Grimes clkinfo.hz = hz; 561df8bae1dSRodney W. Grimes clkinfo.tick = tick; 562df8bae1dSRodney W. Grimes clkinfo.profhz = profhz; 563df8bae1dSRodney W. Grimes clkinfo.stathz = stathz ? stathz : hz; 564df8bae1dSRodney W. Grimes return (sysctl_rdstruct(where, sizep, NULL, &clkinfo, sizeof(clkinfo))); 565df8bae1dSRodney W. Grimes } 566