Files
Arquivotheca.SunOS-4.1.3/sys/os/kern_clock.c
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

955 lines
23 KiB
C

#ident "@(#)kern_clock.c 1.1 92/07/30 SMI; from UCB 7.1 6/5/86"
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/dk.h>
#include <sys/callout.h>
#include <sys/user.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/vm.h>
#include <sys/trace.h>
#include <machine/reg.h>
#include <machine/psl.h>
#include <machine/cpu.h>
#include <machine/param.h>
#ifdef sun4
#include <machine/mmu.h> /* to get MDEVBASE used in clock.h */
#endif
#include <machine/clock.h>
#include <vm/hat.h>
#include <vm/as.h>
#include <vm/rm.h>
#ifdef vax
#include <vax/mtpr.h>
#endif
#ifdef GPROF
#include <sys/gprof.h>
#endif
#ifdef MULTIPROCESSOR
#include "percpu.h"
extern int nmod;
#endif
/*
* Clock handling routines.
*
* This code is written to operate with two timers which run
* independently of each other. The main clock, running at hz
* times per second, is used to do scheduling and timeout calculations.
* The second timer does resource utilization estimation statistically
* based on the state of the machine phz times a second. Both functions
* can be performed by a single clock (ie hz == phz), however the
* statistics will be much more prone to errors. Ideally a machine
* would have separate clocks measuring time spent in user state, system
* state, interrupt state, and idle state. These clocks would allow a non-
* approximate measure of resource utilization.
*/
/*
* TODO:
* time of day, system/user timing, timeouts, profiling on separate timers
* allocate more timeout table slots when table overflows.
*/
#ifdef notdef
/*
* Bump a timeval by a small number of usec's.
*/
bumptime(tp, usec)
register struct timeval *tp;
int usec;
{
tp->tv_usec += usec;
if (tp->tv_usec >= 1000000) {
tp->tv_usec -= 1000000;
tp->tv_sec++;
}
}
#endif notdef
#define BUMPTIME(t, usec) { \
register struct timeval *tp = (t); \
\
tp->tv_usec += (usec); \
if (tp->tv_usec >= 1000000) { \
tp->tv_usec -= 1000000; \
tp->tv_sec++; \
} \
}
#ifdef TRACE
static int traceint = 0;
#define TRACEINT 50
#endif TRACE
extern struct user *uunix;
/*
* The hz hardware interval timer.
* We update the events relating to real time.
* If this timer is also being used to gather statistics,
* we run through the statistics gathering routine as well.
*/
#ifdef MEASURE_KERNELSTACK
/*
* Instrumented to measure the stack pointer
*
*/
int minkernstack = 0;
int minintrstack = 0;
int print_at_all = 0;
int print_minkernstack = 0;
int print_minintrstack = 0;
#include <machine/pte.h>
hardclock(pc, ps, sp)
caddr_t pc;
int ps;
int sp; /* Yes, should be a caddr_t, but making it an int is easier */
#else MEASURE_KERNELSTACK
hardclock(pc, ps)
caddr_t pc;
int ps;
#endif MEASURE_KERNELSTACK
{
register struct callout *p1;
#ifndef MULTIPROCESSOR
register struct proc *p;
register int s;
#endif
int needsoft = 0;
extern int tickdelta;
extern long timedelta;
/*
* doresettodr values:
* 0 - no set pending
* 1 - set when wanting to be sync'd
* other - set chip when time.tv_sec greater
* than or equal to this value
*/
extern int doresettodr;
#ifdef MEASURE_KERNELSTACK
/*
* Find the lowest stack pointer and print it out
*/
if (! USERMODE(ps)) {
if (sp > (int) Sysmap) {
/* On kernel stack */
if (sp < minkernstack) {
minkernstack = sp;
if (print_at_all) {
print_minkernstack = 1;
needsoft = 1;
}
}
} else {
/* On interrupt stack */
if (sp < minintrstack) {
minintrstack = sp;
if (print_at_all) {
print_minintrstack = 1;
needsoft = 1;
}
}
}
}
#endif MEASURE_KERNELSTACK
#ifdef TRACE
/* Calibrate trace */
if (++traceint == TRACEINT) {
traceint = 0;
trace3(TR_CALIBRATE, 0, time.tv_sec, time.tv_usec);
trace3(TR_CALIBRATE, 1, time.tv_sec, time.tv_usec);
}
#endif TRACE
/*
* Update real-time timeout queue.
* At front of queue are some number of events which are ``due''.
* The time to these is <= 0 and if negative represents the
* number of ticks which have passed since it was supposed to happen.
* The rest of the q elements (times > 0) are events yet to happen,
* where the time for each is given as a delta from the previous.
* Decrementing just the first of these serves to decrement the time
* to all events.
*/
p1 = calltodo.c_next;
while (p1) {
if (--p1->c_time > 0)
break;
needsoft = 1;
if (p1->c_time == 0)
break;
p1 = p1->c_next;
}
#ifndef MULTIPROCESSOR
/*
* Charge the time out based on the mode the cpu is in.
* Here again we fudge for the lack of proper interval timers
* assuming that the current state has been around at least
* one tick.
*/
if (USERMODE(ps)) {
if (u.u_prof.pr_scale)
needsoft = 1;
/*
* CPU was in user state. Increment
* user time counter, and process process-virtual time
* interval timer.
*/
BUMPTIME(&u.u_ru.ru_utime, tick);
if (timerisset(&u.u_timer[ITIMER_VIRTUAL].it_value) &&
itimerdecr(&u.u_timer[ITIMER_VIRTUAL], tick) == 0)
psignal(u.u_procp, SIGVTALRM);
} else {
/*
* CPU was in system state.
*/
if (!noproc) {
BUMPTIME(&u.u_ru.ru_stime, tick);
}
}
/*
* If the cpu is currently scheduled to a process, then
* charge it with resource utilization for a tick, updating
* statistics which run in (user+system) virtual time,
* such as the cpu time limit and profiling timers.
* This assumes that the current process has been running
* the entire last tick.
*/
if (!noproc && !(u.u_procp->p_flag & SWEXIT)) {
p = u.u_procp;
if ((u.u_ru.ru_utime.tv_sec+u.u_ru.ru_stime.tv_sec+1) >
u.u_rlimit[RLIMIT_CPU].rlim_cur) {
psignal(p, SIGXCPU);
if (u.u_rlimit[RLIMIT_CPU].rlim_cur <
u.u_rlimit[RLIMIT_CPU].rlim_max)
u.u_rlimit[RLIMIT_CPU].rlim_cur += 5;
}
if (timerisset(&u.u_timer[ITIMER_PROF].it_value) &&
itimerdecr(&u.u_timer[ITIMER_PROF], tick) == 0)
psignal(p, SIGPROF);
s = p->p_rssize = rm_asrss(p->p_as);
u.u_ru.ru_idrss += s;
if (s > u.u_ru.ru_maxrss)
u.u_ru.ru_maxrss = s;
/*
* We adjust the priority of the current process.
* The priority of a process gets worse as it accumulates
* CPU time. The cpu usage estimator (p_cpu) is increased here
* and the formula for computing priorities (in kern_synch.c)
* will compute a different value each time the p_cpu increases
* by 4. The cpu usage estimator ramps up quite quickly when the
* process is running (linearly), and decays away exponentially,
* at a rate which is proportionally slower when the system is
* busy. The basic principal is that the system will 90% forget
* that a process used a lot of CPU time in 5*loadav seconds.
* [See def. of nrscale in kern_synch.c for how this works.]
* This causes the system to favor processes which haven't run
* much recently, and to round-robin among other processes.
* NOTE:
* The scheduling alteration is a non sequitur to kernel
* running lwp's. In fact, if p_pri changes while the
* process is still on the run queue, a panic from remrq
* may result
*/
#ifdef LWP
if (!runthreads) {
#endif LWP
p->p_cpticks++;
if (++p->p_cpu == 0)
p->p_cpu--;
if ((p->p_cpu&3) == 0) {
(void) setpri(p);
if (p->p_pri >= PUSER)
p->p_pri = p->p_usrpri;
}
#ifdef LWP
}
#endif LWP
}
#endif /* !MULTIPROCESSOR */
/*
* If the alternate clock has not made itself known then
* we must gather the statistics.
*/
if (phz == 0)
gatherstats(pc, ps);
#ifdef MULTIPROCESSOR
charge_time(&needsoft);
#endif
/*
* Increment the time-of-day, and schedule
* processing of the callouts at a very low cpu priority,
* so we don't keep the relatively high clock interrupt
* priority any longer than necessary.
*/
if (timedelta == 0) {
if (doresettodr == 1) {
/*
* Oh dear- somebody zeroed timedelta out from
* underneath an adjustment that was in progress.
* Better zero doresettodr to be sure...
*/
doresettodr = 0;
}
BUMPTIME(&time, tick);
} else {
register delta;
if (timedelta < 0) {
delta = tick - tickdelta;
timedelta += tickdelta;
} else {
delta = tick + tickdelta;
timedelta -= tickdelta;
}
BUMPTIME(&time, delta);
if (-tickdelta < timedelta && timedelta < tickdelta) {
timedelta = 0;
if (doresettodr == 1) {
doresettodr = time.tv_sec + 1;
}
}
}
if (doresettodr > 1 && doresettodr <= time.tv_sec) {
doresettodr = 0;
resettodr();
}
if (needsoft) {
if (BASEPRI(ps)) {
/*
* Save the overhead of a software interrupt;
* it will happen as soon as we return, so do it now.
*/
(void) splsoftclock();
softclock(ps);
} else {
#ifdef sun
int softclock();
/*
* Pass softclock only the system mode bit
* to avoid the softcall table from overflowing
* from the various different calls to softclock.
*/
softcall(softclock, (caddr_t)(ps & SR_SMODE));
#endif
#ifdef vax
setsoftclock();
#endif
}
}
}
#ifdef MULTIPROCESSOR
/*
* Charge cpu time to all processes associated with a processor.
* *needsoftp is set to 1 if a softcall is required. This is set for
* the calling routine's (hardclock()) benefit.
*/
charge_time(needsoftp)
int *needsoftp;
{
register int cix, s;
register struct user *up;
register struct proc *p;
/*
* For each processor...
*/
for (cix = 0; cix < nmod; cix++) {
if (!PerCPU[cix].cpu_exists ||
PerCPU[cix].noproc)
continue;
/*
* cpu_idle is set by the idle process when it is sure
* that nothing is going on.
*/
if (PerCPU[cix].cpu_idle)
continue;
up = PerCPU[cix].uunix;
if (up == NULL)
continue;
p = up->u_procp;
if (p == NULL)
continue;
if (p == &idleproc)
continue;
/*
* Charge the time out based on the mode the cpu is in. Here
* again we fudge for the lack of proper interval timers
* assuming that the current state has been around at least
* one tick.
*
* XXX - of course, Galaxy has proper interval timers; do we
* have the rocket-science time to gut all this stuff and make
* it all work?
*/
if (PerCPU[cix].cpu_supv) {
BUMPTIME(&up->u_ru.ru_stime, tick);
} else {
if (up->u_prof.pr_scale)
*needsoftp = 1;
/*
* Increment user time counter, and process
* process-virtual time interval timer.
*/
BUMPTIME(&up->u_ru.ru_utime, tick);
if ((timerisset(&up->u_timer[ITIMER_VIRTUAL].it_value)) &&
(itimerdecr(&up->u_timer[ITIMER_VIRTUAL], tick) == 0))
psignal(p, SIGVTALRM);
}
/*
* If the cpu is currently scheduled to a process, then
* charge it with resource utilization for a tick, updating
* statistics which run in (user+system) virtual time, such
* as the cpu time limit and profiling timers. This assumes
* that the current process has been running the entire last
* tick.
*/
if (!(p->p_flag & SWEXIT)) {
if ((up->u_ru.ru_utime.tv_sec + up->u_ru.ru_stime.tv_sec + 1) >
up->u_rlimit[RLIMIT_CPU].rlim_cur) {
psignal(p, SIGXCPU);
if (up->u_rlimit[RLIMIT_CPU].rlim_cur <
up->u_rlimit[RLIMIT_CPU].rlim_max)
up->u_rlimit[RLIMIT_CPU].rlim_cur += 5;
}
if (timerisset(&up->u_timer[ITIMER_PROF].it_value) &&
itimerdecr(&up->u_timer[ITIMER_PROF], tick) == 0)
psignal(p, SIGPROF);
s = p->p_rssize = rm_asrss(p->p_as);
up->u_ru.ru_idrss += s;
if (s > up->u_ru.ru_maxrss)
up->u_ru.ru_maxrss = s;
/*
* We adjust the priority of the current process. The
* priority of a process gets worse as it accumulates
* CPU time. The cpu usage estimator (p_cpu) is
* increased here and the formula for computing
* priorities (in kern_synch.c) will compute a
* different value each time the p_cpu increases by
* 4. The cpu usage estimator ramps up quite quickly
* when the process is running (linearly), and decays
* away exponentially, at a rate which is
* proportionally slower when the system is busy.
* The basic principal is that the system will 90%
* forget that a process used a lot of CPU time in
* 5*loadav seconds. [See def. of nrscale in
* kern_synch.c for how this works.] This causes the
* system to favor processes which haven't run much
* recently, and to round-robin among other
* processes. NOTE: The scheduling alteration is a
* non sequitur to kernel running lwp's. In fact, if
* p_pri changes while the process is still on the
* run queue, a panic from remrq may result
*/
#ifdef LWP
if (!runthreads)
#endif /* LWP */
{
/*
* It is possible that this process has been
* placed back on the run queue and that the
* CPU has not changed its uunix pointer, so
* do the right thing ...
*/
int wasonq = onrq(p);
p->p_cpticks++;
if (++p->p_cpu == 0)
p->p_cpu--;
if ((p->p_cpu & 3) == 0) {
(void) setpri(p);
if (p->p_pri >= PUSER) {
if (wasonq)
remrq(p);
p->p_pri = p->p_usrpri;
if (wasonq)
setrq(p);
}
}
}
}
}
}
#endif /* MULTIPROCESSOR */
int dk_ndrive = DK_NDRIVE;
/*
* Gather statistics on resource utilization.
*
* We make a gross assumption: that the system has been in the
* state it is in (user state, kernel state, interrupt state,
* or idle state) for the entire last time interval, and
* update statistics accordingly.
*
* In the MP version, only one processor per hardclock interrupt (level
* 10) calls this routine. Therefore, one
* processor collects CPU time data for all of the processors present.
*
*/
/*ARGSUSED*/
#ifdef MULTIPROCESSOR
gatherstats(pc, ps)
caddr_t pc;
int ps;
{
extern int procset;
register int cix, cs;
register struct user *up;
register struct proc *pp;
/*
* Gather stats for each processor.
*/
for (cix=0; cix < nmod; ++cix)
if ((procset & (1<<cix)) &&
(PerCPU[cix].cpu_enable) &&
(PerCPU[cix].cpu_exists)) {
if (PerCPU[cix].cpu_idle)
cs = CP_IDLE;
else if (PerCPU[cix].cpu_supv)
cs = CP_SYS;
else if ((PerCPU[cix].noproc) ||
((up = PerCPU[cix].uunix) == NULL) ||
(up == &idleuarea) ||
((pp = PerCPU[cix].masterprocp) == NULL) ||
(pp == &idleproc))
cs = CP_IDLE;
else if (pp->p_nice > PZERO)
cs = CP_NICE;
else
cs = CP_USER;
mp_time[cix][cs]++;
cp_time[cs]++;
#ifdef XP_SPIN
if ((cs == CP_SYS) &&
(PerCPU[cix].cpu_supv == 2))
xp_time[cix][XP_SPIN] ++;
#endif XP_SPIN
#ifdef XP_DISK
if ((cs == CP_IDLE) &&
(dk_busy != 0))
xp_time[cix][XP_DISK] ++;
#endif XP_DISK
#ifdef XP_SERV
if ((cs == CP_SYS) &&
(PerCPU[cix].cpu_supv == 3))
xp_time[cix][XP_SERV] ++;
#endif XP_SERV
}
/*
* Collect disk busy data.
*/
for (cix = 0; cix < dk_ndrive; cix++)
if (dk_busy & (1 << cix))
dk_time[cix]++;
}
#else /* !MULTIPROCESSOR */
gatherstats(pc, ps)
caddr_t pc;
int ps;
{
register int cpstate, s;
/*
* Determine what state the cpu is in.
*/
if (USERMODE(ps)) {
/*
* CPU was in user state.
*/
if (u.u_procp->p_nice > NZERO)
cpstate = CP_NICE;
else
cpstate = CP_USER;
} else {
/*
* CPU was in system state. If profiling kernel
* increment a counter. If no process is running
* then this is a system tick if we were running
* at a non-zero IPL (in a driver). If a process is running,
* then we charge it with system time even if we were
* at a non-zero IPL, since the system often runs
* this way during processing of system calls.
* This is approximate, but the lack of true interval
* timers makes doing anything else difficult.
*/
cpstate = CP_SYS;
if (noproc && BASEPRI(ps))
cpstate = CP_IDLE;
#ifdef GPROF
#ifdef sun
/*
* For Sun, this stuff is done in sun/kprof.s
*/
#else
s = pc - s_lowpc;
if (profiling < 2 && s < s_textsize)
kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
#endif
#endif
}
/*
* We maintain statistics shown by user-level statistics
* programs: the amount of time in each cpu state, and
* the amount of time each of dk_ndrive ``drives'' is busy.
*/
cp_time[cpstate]++;
for (s = 0; s < dk_ndrive; s++)
if (dk_busy & (1 << s))
dk_time[s]++;
}
#endif MULTIPROCESSOR
#ifdef MULTIPROCESSOR
static
do_SOWEUPC()
{
register struct user *up;
register struct proc *p;
if ((up = uunix) && (p = up->u_procp)) {
p->p_flag |= SOWEUPC;
aston();
}
}
#endif MULTIPROCESSOR
/*
* Software priority level clock interrupt.
* Run periodic events from timeout queue.
*/
softclock(ps)
int ps;
{
#ifdef MULTIPROCESSOR
int cix;
struct user *up;
#endif MULTIPROCESSOR
#ifdef MEASURE_KERNELSTACK
/*
* Print out stack statistics, if necessary
*/
if (print_minkernstack) {
register int s;
register int sp;
s = splclock();
print_minkernstack = 0;
sp = minkernstack;
(void) splx(s);
printf("minkernstack = 0x%x\n", sp);
}
if (print_minintrstack) {
register int s;
register int sp;
s = splclock();
print_minintrstack = 0;
sp = minintrstack;
(void) splx(s);
printf("minintrstack = 0x%x\n", sp);
}
#endif MEASURE_KERNELSTACK
for (;;) {
register struct callout *p1;
register caddr_t arg;
register int (*func)();
register int a, s;
s = splclock();
if (((p1 = calltodo.c_next) == 0) || (p1->c_time > 0)) {
(void) splx(s);
break;
}
arg = p1->c_arg; func = p1->c_func; a = p1->c_time;
calltodo.c_next = p1->c_next;
p1->c_next = callfree;
callfree = p1;
(void) splx(s);
(*func)(arg, a);
}
/*
* If trapped user-mode and profiling, give it
* a profiling tick.
*/
if (USERMODE(ps)) {
register struct proc *p = u.u_procp;
if (u.u_prof.pr_scale) {
p->p_flag |= SOWEUPC;
aston();
}
}
#ifdef MULTIPROCESSOR
/*
* If any other cpu is profiling, give it a profiling
* tick if it is in user mode.
*/
for (cix = 0; cix < nmod; cix++) {
if ((cix != cpuid) &&
(procset & (1<<cix)) &&
PerCPU[cix].cpu_exists &&
!PerCPU[cix].noproc &&
(up = PerCPU[cix].uunix) &&
(up != &idleuarea) &&
up->u_prof.pr_scale)
xc_oncpu(0, 0, 0, 0,
cix, do_SOWEUPC);
}
#endif MULTIPROCESSOR
}
/*
* Arrange that (*fun)(arg) is called in t/hz seconds.
*/
timeout(fun, arg, t)
int (*fun)();
caddr_t arg;
register int t;
{
register struct callout *p1, *p2, *pnew;
register int s = splclock();
if (t <= 0)
t = 1;
pnew = callfree;
if (pnew == NULL)
panic("timeout table overflow");
callfree = pnew->c_next;
pnew->c_arg = arg;
pnew->c_func = fun;
for (p1 = &calltodo; (p2 = p1->c_next) && p2->c_time < t; p1 = p2)
if (p2->c_time > 0)
t -= p2->c_time;
p1->c_next = pnew;
pnew->c_next = p2;
pnew->c_time = t;
if (p2)
p2->c_time -= t;
(void) splx(s);
}
/*
* untimeout is called to remove a function timeout call
* from the callout structure.
*/
untimeout(fun, arg)
int (*fun)();
caddr_t arg;
{
register struct callout *p1, *p2;
register int s;
s = splclock();
for (p1 = &calltodo; (p2 = p1->c_next) != 0; p1 = p2) {
if (p2->c_func == fun && p2->c_arg == arg) {
if (p2->c_next && p2->c_time > 0)
p2->c_next->c_time += p2->c_time;
p1->c_next = p2->c_next;
p2->c_next = callfree;
callfree = p2;
break;
}
}
(void) splx(s);
}
/*
* MICROFUDGE is used by "hzto" to relax its calculation
* just a little bit; if an integer number of ticks take
* us to less than MICROFUDGE microseconds before the
* destination time, we call it "enough" and do not wait
* the extra tick.
*/
#ifndef MICROFUDGE
#define MICROFUDGE 10
#endif
/*
* Compute number of hz until specified time.
* Used to compute third argument to timeout() from an
* absolute time.
*/
hzto(tv)
struct timeval *tv;
{
register long ticks, sec, usec;
int s = splclock();
/*
* Compute number of ticks we will see between now and
* the target time; returns "1" if the destination time
* is before the next tick, so we always get some delay,
* and returns 0x7fffffff ticks if we would overflow.
*/
sec = tv->tv_sec - time.tv_sec;
usec = tv->tv_usec - time.tv_usec;
if (usec < 0) {
sec--;
usec += 1000000;
}
ticks = usec * hz / 1000000; /* integer ticks in usec delay */
usec -= (ticks * 1000000) / hz; /* ticks remaining */
if (usec > MICROFUDGE)
ticks++;
/*
* Compute ticks, accounting for negative and overflow as above.
* Overflow protection kicks in at about 70 weeks for hz=50
* and at about 35 weeks for hz=100.
*/
if ((sec < 0) || ((sec == 0) && (ticks < 1)))
ticks = 1; /* protect vs nonpositive */
else if (sec > (0x7fffffff - ticks) / hz)
ticks = 0x7fffffff; /* protect vs overflow */
else
ticks += sec * hz; /* common case */
(void) splx(s);
return (ticks);
}
profil()
{
register struct a {
short *bufbase;
unsigned bufsize;
unsigned pcoffset;
unsigned pcscale;
} *uap = (struct a *)u.u_ap;
register struct uprof *upp = &u.u_prof;
/*
* The low-order bit of the scale is irrelevant anyway, and a scale of
* 1 is supposed to be equivalent to a scale of 0 (both should turn
* profiling off), so we clear the low-order bit.
*/
upp->pr_base = uap->bufbase;
upp->pr_size = uap->bufsize;
upp->pr_off = uap->pcoffset;
upp->pr_scale = uap->pcscale & ~01;
}
#if defined(SUN4_330) || defined(SUN4_470) || defined(sun4c) || defined(sun4m)
#define HI_RES_CLOCK
#endif
uniqtime(tv)
register struct timeval *tv;
{
static struct timeval last; /* last value returned */
static struct timeval now; /* current time value */
#ifdef HI_RES_CLOCK
register int cr; /* nsec counter register */
register int ib; /* usec past last tick */
#endif
int s;
s = splclock(); /* protect accesses to now, last, time */
now = time;
#ifdef HI_RES_CLOCK
/*
* Some models have a high resolution timer that we can use to
* figure out the microsecond-precision time.
*
* If the "limit reached" bit appears in the value register and is
* turned on, we have a pending interrupt; add a tick's worth of time
* to the clock.
*
* If the "limit reached" bit is not reflected in the counter value
* register, then this may be slow by 1/100 sec if there is a pending
* clock interrupt; reading the bit from the limit register would be a
* bad thing.
*
* The limit bit may turn on AS WE REACH the limit. Be ready
* to handle both cases here.
*
* XXX - it would be nice to be able to have this code figure
* out if the clock is supported and if it is there without
* looking at "cpu".
*/
#if !defined(sun4c) && !defined(sun4m)
switch (cpu) {
#ifdef SUN4_330
case CPU_SUN4_330:
#endif
#ifdef SUN4_470
case CPU_SUN4_470:
#endif
#endif /* !sun4c && !sun4m */
cr = COUNTER->counter10;
ib = (cr & CTR_USEC_MASK) >> CTR_USEC_SHIFT;
if (cr & CTR_LIMIT_BIT)
ib = (ib % tick) + tick;
BUMPTIME(&now, ib);
#if !defined(sun4c) && !defined(sun4m)
break;
default: /* kernel supports, cpu does not */
break;
}
#endif /* !sun4c && !sun4m */
#endif /* HI_RES_CLOCK */
/*
* Try to keep the return value of uniqtime() monotonicly
* increasing in the face of repeated calls, but don't be
* obsessive about it in the face of large differences.
*/
if ((now.tv_sec > last.tv_sec) || /* higher second, or */
((now.tv_sec == last.tv_sec) && /* same second and */
(now.tv_usec > last.tv_usec)) || /* higher microsecond, or */
((last.tv_sec - now.tv_sec) > 5)) { /* definitely back in time */
last = now;
} else {
BUMPTIME(&last, 1); /* keep it monotonicly ascending */
}
*tv = last;
(void) splx(s);
}