+/*
+ * Copyright (c) 1982, 1986, 1990 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ * @(#)kern_synch.c 7.11 (Berkeley) %G%
+ */
+
+#include "machine/pte.h"
+#include "machine/psl.h"
+#include "machine/mtpr.h"
+
+#include "param.h"
+#include "systm.h"
+#include "user.h"
+#include "proc.h"
+#include "vm.h"
+#include "kernel.h"
+#include "buf.h"
+
+/*
+ * Force switch among equal priority processes every 100ms.
+ */
+roundrobin()
+{
+
+ runrun++;
+ aston();
+ timeout(roundrobin, (caddr_t)0, hz / 10);
+}
+
+/*
+ * constants for digital decay and forget
+ * 90% of (p_cpu) usage in 5*loadav time
+ * 95% of (p_pctcpu) usage in 60 seconds (load insensitive)
+ * Note that, as ps(1) mentions, this can let percentages
+ * total over 100% (I've seen 137.9% for 3 processes).
+ *
+ * Note that hardclock updates p_cpu and p_cpticks independently.
+ *
+ * We wish to decay away 90% of p_cpu in (5 * loadavg) seconds.
+ * That is, the system wants to compute a value of decay such
+ * that the following for loop:
+ * for (i = 0; i < (5 * loadavg); i++)
+ * p_cpu *= decay;
+ * will compute
+ * p_cpu *= 0.1;
+ * for all values of loadavg:
+ *
+ * Mathematically this loop can be expressed by saying:
+ * decay ** (5 * loadavg) ~= .1
+ *
+ * The system computes decay as:
+ * decay = (2 * loadavg) / (2 * loadavg + 1)
+ *
+ * We wish to prove that the system's computation of decay
+ * will always fulfill the equation:
+ * decay ** (5 * loadavg) ~= .1
+ *
+ * If we compute b as:
+ * b = 2 * loadavg
+ * then
+ * decay = b / (b + 1)
+ *
+ * We now need to prove two things:
+ * 1) Given factor ** (5 * loadavg) ~= .1, prove factor == b/(b+1)
+ * 2) Given b/(b+1) ** power ~= .1, prove power == (5 * loadavg)
+ *
+ * Facts:
+ * For x close to zero, exp(x) =~ 1 + x, since
+ * exp(x) = 0! + x**1/1! + x**2/2! + ... .
+ * therefore exp(-1/b) =~ 1 - (1/b) = (b-1)/b.
+ * For x close to zero, ln(1+x) =~ x, since
+ * ln(1+x) = x - x**2/2 + x**3/3 - ... -1 < x < 1
+ * therefore ln(b/(b+1)) = ln(1 - 1/(b+1)) =~ -1/(b+1).
+ * ln(.1) =~ -2.30
+ *
+ * Proof of (1):
+ * Solve (factor)**(power) =~ .1 given power (5*loadav):
+ * solving for factor,
+ * ln(factor) =~ (-2.30/5*loadav), or
+ * factor =~ exp(-1/((5/2.30)*loadav) =~ exp(-1/(2*loadav)) =
+ * exp(-1/b) =~ (b-1)/b =~ b/(b+1). QED
+ *
+ * Proof of (2):
+ * Solve (factor)**(power) =~ .1 given factor == (b/(b+1)):
+ * solving for power,
+ * power*ln(b/(b+1)) =~ -2.30, or
+ * power =~ 2.3 * (b + 1) = 4.6*loadav + 2.3 =~ 5*loadav. QED
+ *
+ * Actual power values for the implemented algorithm are as follows:
+ * loadav: 1 2 3 4
+ * power: 5.68 10.32 14.94 19.55
+ */
+
+/* calculations for digital decay to forget 90% of usage in 5*loadav sec */
+#define get_b(loadav) (2 * (loadav))
+#define get_pcpu(b, cpu) (((b) * ((cpu) & 0377)) / ((b) + FSCALE))
+
+/* decay 95% of `p_pctcpu' in 60 seconds; see CCPU_SHIFT before changing */
+fixpt_t ccpu = 0.95122942450071400909 * FSCALE; /* exp(-1/20) */
+
+/*
+ * If `ccpu' is not equal to `exp(-1/20)' and you still want to use the
+ * faster/more-accurate formula, you'll have to estimate CCPU_SHIFT below
+ * and possibly adjust FSHIFT in "param.h" so that (FSHIFT >= CCPU_SHIFT).
+ *
+ * To estimate CCPU_SHIFT for exp(-1/20), the following formula was used:
+ * 1 - exp(-1/20) ~= 0.0487 ~= 0.0488 == 1 (fixed pt, *11* bits).
+ *
+ * If you dont want to bother with the faster/more-accurate formula, you
+ * can set CCPU_SHIFT to (FSHIFT + 1) which will use a slower/less-accurate
+ * (more general) method of calculating the %age of CPU used by a process.
+ */
+#define CCPU_SHIFT 11
+
+/*
+ * Recompute process priorities, once a second
+ */
+schedcpu()
+{
+ register fixpt_t b = get_b(averunnable[0]);
+ register struct proc *p;
+ register int s, a;
+
+ wakeup((caddr_t)&lbolt);
+ for (p = allproc; p != NULL; p = p->p_nxt) {
+ if (p->p_time != 127)
+ p->p_time++;
+ if (p->p_stat==SSLEEP || p->p_stat==SSTOP)
+ if (p->p_slptime != 127)
+ p->p_slptime++;
+ p->p_pctcpu = (p->p_pctcpu * ccpu) >> FSHIFT;
+ /*
+ * If the process has slept the entire second,
+ * stop recalculating its priority until it wakes up.
+ */
+ if (p->p_slptime > 1)
+ continue;
+ /*
+ * p_pctcpu is only for ps.
+ */
+#if (FSHIFT >= CCPU_SHIFT)
+ p->p_pctcpu += (hz == 100)?
+ ((fixpt_t) p->p_cpticks) << (FSHIFT - CCPU_SHIFT):
+ 100 * (((fixpt_t) p->p_cpticks)
+ << (FSHIFT - CCPU_SHIFT)) / hz;
+#else
+ p->p_pctcpu += ((FSCALE - ccpu) *
+ (p->p_cpticks * FSCALE / hz)) >> FSHIFT;
+#endif
+ p->p_cpticks = 0;
+ a = (int) get_pcpu(b, p->p_cpu) + p->p_nice;
+ if (a < 0)
+ a = 0;
+ if (a > 255)
+ a = 255;
+ p->p_cpu = a;
+ (void) setpri(p);
+ s = splhigh(); /* prevent state changes */
+ if (p->p_pri >= PUSER) {
+#define PPQ (128 / NQS)
+ if ((p != u.u_procp || noproc) &&
+ p->p_stat == SRUN &&
+ (p->p_flag & SLOAD) &&
+ (p->p_pri / PPQ) != (p->p_usrpri / PPQ)) {
+ remrq(p);
+ p->p_pri = p->p_usrpri;
+ setrq(p);
+ } else
+ p->p_pri = p->p_usrpri;
+ }
+ splx(s);
+ }
+ vmmeter();
+ if (runin!=0) {
+ runin = 0;
+ wakeup((caddr_t)&runin);
+ }
+ if (bclnlist != NULL)
+ wakeup((caddr_t)&proc[2]);
+ timeout(schedcpu, (caddr_t)0, hz);
+}
+
+/*
+ * Recalculate the priority of a process after it has slept for a while.
+ */
+updatepri(p)
+ register struct proc *p;
+{
+ register int a = p->p_cpu & 0377;
+ register fixpt_t b = get_b(averunnable[0]);
+
+ p->p_slptime--; /* the first time was done in schedcpu */
+ while (a && --p->p_slptime)
+ a = (int) get_pcpu(b, a) /* + p->p_nice */;
+ p->p_slptime = 0;
+ if (a < 0)
+ a = 0;
+ if (a > 255)
+ a = 255;
+ p->p_cpu = a;
+ (void) setpri(p);
+}