/*
 *	SCCS id	@(#)clock.c	2.1 (Berkeley)	9/1/83
 */

#include "param.h"
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/seg.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/reg.h>
#ifdef UCB_METER
#include <sys/text.h>
#include <sys/vm.h>
#endif
#ifdef UCB_NET
	/*
	 * shifted to keep "make depend" from finding these for now...
	 */
#	include <sys/protosw.h>
#	include <sys/socket.h>
#	include "../net/if.h"
#	include "../net/in_systm.h"
#endif

#ifdef UCB_FRCSWAP
extern int	idleflg;	/* If set, allow incore forks and expands */
				/* Set before idle(), cleared in clock.c */
#endif

#ifdef  UCB_NET
/*
 * Protoslow is like lbolt, but for slow protocol timeouts, counting
 * up to (hz/PR_SLOWHZ), then causing a pfslowtimo().
 * Protofast is like lbolt, but for fast protocol timeouts, counting
 * up to (hz/PR_FASTHZ), then causing a pffasttimo().
 */
int	protoslow;
int	protofast;
int	ifnetslow;
int     netoff;
#endif

#define	SCHMAG	8/10

/*
 * clock is called straight from
 * the real time clock interrupt.
 *
 * Functions:
 *	reprime clock
 *	copy *switches to display
 *	implement callouts
 *	maintain user/system times
 *	maintain date
 *	profile
 *	lightning bolt wakeup (every second)
 *	alarm clock signals
 *	jab the scheduler
 */

/*ARGSUSED*/
#ifdef	MENLO_KOV
clock(dev, sp, r1, ov, nps, r0, pc, ps)
#else
clock(dev, sp, r1, nps, r0, pc, ps)
#endif
dev_t dev;
caddr_t pc;
{
	register a;
	mapinfo	map;
	extern caddr_t waitloc;
	extern char *panicstr;

	/*
	 * restart clock
	 */
#if	PDP11 == 21
	while (*((int *)0173024) & 0200) ;
	a = *((int *)0173030);
#else
	if(lks)
		*lks = 0115;
#endif
	/*
	 * ensure normal mapping of kernel data
	 */
	savemap(map);

#ifdef	DISPLAY
	/*
	 * display register
	 */
	display();
#endif

	/*
	 * callouts
	 * never after panics
	 * if none, just continue
	 * else update first non-zero time
	 */
	if (panicstr == (char *) 0) {
		register struct callout *p1, *p2;

		if(callout[0].c_func == NULL)
			goto out;
		p2 = &callout[0];
		while(p2->c_time<=0 && p2->c_func!=NULL)
			p2++;
		p2->c_time--;

#ifdef  UCB_NET
		/*
		 * Time moves on for protocols.
		 */
		if(!netoff) {
			--protoslow; --protofast; --ifnetslow;
			if(protoslow<=0 || protofast<=0 || ifnetslow<=0)
				schednetisr(NETISR_CLOCK);
		}
#endif

		/*
		 * if ps is high, just return
		 */
		if (!BASEPRI(ps))
			goto out;

		/*
		 * callout
		 */
		if(callout[0].c_time <= 0) {
			p1 = &callout[0];
			while(p1->c_func != 0 && p1->c_time <= 0) {
				(*p1->c_func)(p1->c_arg);
				p1++;
			}
			p2 = &callout[0];
			while(p2->c_func = p1->c_func) {
				p2->c_time = p1->c_time;
				p2->c_arg = p1->c_arg;
				p1++;
				p2++;
			}
		}
	}
out:

	a = (dk_busy != 0);
	if (USERMODE(ps)) {
		u.u_utime++;
		if(u.u_prof.pr_scale)
			addupc(pc, &u.u_prof, 1);
		if(u.u_procp->p_nice > NZERO)
			a += 2;
	} else {
		a += 4;
		if (pc == waitloc)
			a += 2;
		u.u_stime++;
	}
	sy_time[a] += 1;
	dk_time[dk_busy & ((1 << ndisk) - 1)] += 1;
	{
	  register struct proc *pp;
	  pp = u.u_procp;
	  if(++pp->p_cpu == 0)
		pp->p_cpu--;
	}
	/*
	 * lightning bolt time-out
	 * and time of day
	 */
	if ((++lbolt >= hz) && (BASEPRI(ps))) {
		register struct proc *pp;

		lbolt -= hz;
#ifdef	UCB_FRCSWAP
		idleflg = 0;
#endif
		++time;
		(void) _spl1();
#if defined(UCB_LOAD) || defined(UCB_METER)
		meter();
#endif
		runrun++;
		wakeup((caddr_t)&lbolt);
		for(pp = &proc[0]; pp <= maxproc; pp++)
		if (pp->p_stat && pp->p_stat<SZOMB) {
			if(pp->p_time != 127)
				pp->p_time++;
#ifdef	UCB_METER
			if (pp->p_stat == SSLEEP || pp->p_stat == SSTOP)
				if (pp->p_slptime != 127)
					pp->p_slptime++;
#endif	UCB_METER
			if (pp->p_clktim && --pp->p_clktim == 0)
#ifdef	UCB_NET
				/*
				 * If process has clock counting down, and it
				 * expires, set it running (if this is a
				 * tsleep()), or give it an SIGALRM (if the user
				 * process is using alarm signals.
				 */
				if (pp->p_flag & STIMO) {
					a = spl6();
					switch (pp->p_stat) {

					case SSLEEP:
						setrun(pp);
						break;

					case SSTOP:
						unsleep(pp);
						break;
					}
					pp->p_flag &= ~STIMO;
					splx(a);
				} else
#endif
					psignal(pp, SIGALRM);
			a = (pp->p_cpu & 0377) * SCHMAG + pp->p_nice - NZERO;
			if(a < 0)
				a = 0;
			if(a > 255)
				a = 255;
			pp->p_cpu = a;
			if(pp->p_pri >= PUSER)
				setpri(pp);
		}
		if(runin!=0) {
			runin = 0;
			wakeup((caddr_t)&runin);
		}
	}
	restormap(map);
}

/*
 * timeout is called to arrange that
 * fun(arg) is called in tim/hz seconds.
 * An entry is sorted into the callout
 * structure. The time in each structure
 * entry is the number of hz's more
 * than the previous entry.
 * In this way, decrementing the
 * first entry has the effect of
 * updating all entries.
 *
 * The panic is there because there is nothing
 * intelligent to be done if an entry won't fit.
 */
timeout(fun, arg, tim)
int (*fun)();
caddr_t arg;
{
	register struct callout *p1, *p2;
	register int t;
	int s;

	t = tim;
	p1 = &callout[0];
	s = spl7();
	while(p1->c_func != 0 && p1->c_time <= t) {
		t -= p1->c_time;
		p1++;
	}
	p1->c_time -= t;
	p2 = p1;
	while(p2->c_func != 0)
		p2++;
	if (p2 >= callNCALL)
		panic("Timeout table overflow");
	while(p2 >= p1) {
		(p2+1)->c_time = p2->c_time;
		(p2+1)->c_func = p2->c_func;
		(p2+1)->c_arg = p2->c_arg;
		p2--;
	}
	p1->c_time = t;
	p1->c_func = fun;
	p1->c_arg = arg;
	splx(s);
	return;
}

#if defined(UCB_LOAD) || defined(UCB_METER)
/*
 * Count up various things once a second
 */
short avenrun[3];	/* internal load average in psuedo-floating point */
#define ave(smooth,new,time)  (smooth) = (((time)-1) * (smooth) + (new))/ (time)

#define	ctok(cliks)	(((cliks) >> 4) & 07777)	/* clicks to KB */

meter()
{
#ifdef UCB_METER
	register unsigned *cp, *rp;
	register long *sp;

	ave(avefree, ctok(freemem), 5);
#endif

	if (time % 5 == 0) {
		vmtotal();
#ifdef UCB_METER
		cp = &cnt.v_first; rp = &rate.v_first; sp = &sum.vs_first;
		while (cp <= &cnt.v_last) {
			*rp = *cp;
			*sp += *cp;
			*cp = 0;
			rp++, cp++, sp++;
		}
#endif
	}
}

vmtotal()
{
	extern	char	counted[];
	register struct proc *p;
	register struct text *xq;
	register nrun = 0;
#ifdef UCB_METER
	int nt;

	total.t_vmtxt = 0;
	total.t_avmtxt = 0;
	total.t_rmtxt = 0;
	total.t_armtxt = 0;
	for (xq = text; xq < textNTEXT; xq++) {
		counted[xq-text]=0;
		if (xq->x_iptr) {
			total.t_vmtxt += xq->x_size;
			if (xq->x_ccount)
				total.t_rmtxt += xq->x_size;
		}
	}
	total.t_vm = 0;
	total.t_avm = 0;
	total.t_rm = 0;
	total.t_arm = 0;
	total.t_rq = 0;
	total.t_dw = 0;
	total.t_sl = 0;
	total.t_sw = 0;
#endif
	for (p = &proc[1]; p <= maxproc; p++) {
		if (p->p_stat) {
#ifdef UCB_METER
#ifndef	VIRUS_VFORK
			total.t_vm += p->p_size;
			if (p->p_flag & SLOAD)
				total.t_rm += p->p_size;
#else
			total.t_vm += p->p_dsize + p->p_ssize + USIZE;
			if (p->p_flag & SLOAD)
				total.t_rm += p->p_dsize + p->p_ssize + USIZE;
#endif
#endif
			switch (p->p_stat) {

			case SSLEEP:
			case SSTOP:
				if (p->p_pri <= PZERO)
					nrun++;
#ifdef UCB_METER
				if (p->p_flag & SLOAD) {
					if (p->p_pri <= PZERO)
						total.t_dw++;
					else if (p->p_slptime < MAXSLP)
						total.t_sl++;
				} else if (p->p_slptime < MAXSLP)
					total.t_sw++;
				if (p->p_slptime < MAXSLP)
					goto active;
#endif
				break;

			case SRUN:
			case SIDL:
				nrun++;
#ifdef UCB_METER
				if (p->p_flag & SLOAD)
					total.t_rq++;
				else
					total.t_sw++;
active:
#ifndef	VIRUS_VFORK
				total.t_avm += p->p_size;
				if (p->p_flag & SLOAD)
					total.t_arm += p->p_size;
#else
				total.t_avm += p->p_dsize + p->p_ssize + USIZE;
				if (p->p_flag & SLOAD)
					total.t_arm +=
						p->p_dsize + p->p_ssize + USIZE;
#endif
				if (p->p_textp) {
					total.t_avmtxt += p->p_textp->x_size;
					nt = p->p_textp-text;
					if (counted[nt]==0) {
						counted[nt]=1;
						if (p->p_textp->x_ccount)
							total.t_armtxt +=
							    p->p_textp->x_size;
					}
				}
#endif
				break;
			}
		}
	}
#ifdef UCB_METER
	total.t_vm += total.t_vmtxt;
	total.t_avm += total.t_avmtxt;
	total.t_rm += total.t_rmtxt;
	total.t_arm += total.t_armtxt;
	total.t_free = avefree;
#endif
	loadav(avenrun, nrun);
}

/*
 * Compute Tenex style load average.  This code is adapted from similar
 * code by Bill Joy on the Vax system.  The major change is that we
 * avoid floating point since not all pdp-11's have it.  This makes
 * the code quite hard to read - it was derived with some algebra.
 *
 * "floating point" numbers here are stored in a 16 bit short, with
 * 8 bits on each side of the decimal point.  Some partial products
 * will have 16 bits to the right.
 */

	/*
	 * The Vax algorithm is:
	 *
	 * /*
	 *  * Constants for averages over 1, 5, and 15 minutes
	 *  * when sampling at 5 second intervals.
	 *  * /
	 * double	cexp[3] = {
	 * 	0.9200444146293232,	/* exp(-1/12) * /
	 * 	0.9834714538216174,	/* exp(-1/60) * /
	 * 	0.9944598480048967,	/* exp(-1/180) * /
	 * };
	 * 
	 * /*
	 *  * Compute a tenex style load average of a quantity on
	 *  * 1, 5 and 15 minute intervals.
	 *  * /
	 * loadav(avg, n)
	 * 	register double *avg;
	 * 	int n;
	 * {
	 * 	register int i;
	 * 
	 * 	for (i = 0; i < 3; i++)
	 * 		avg[i] = cexp[i] * avg[i] + n * (1.0 - cexp[i]);
	 * }
	 */

long cexp[3] = {
	0353,	/* 256*exp(-1/12) */
	0373,	/* 256*exp(-1/60) */
	0376,	/* 256*exp(-1/180) */
};

loadav(avg, n)
	register short *avg;
	register n;
{
	register int i;

	for (i = 0; i < 3; i++)
		avg[i] = (cexp[i] * (avg[i]-(n<<8)) + (((long)n)<<16)) >> 8;
}
#endif
