BSD 3 development
[unix-history] / usr / src / sys / sys / vmsched.c
/* vmsched.c 2.2 2/10/80 */
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/seg.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/text.h"
#include "../h/vm.h"
#include "../h/cmap.h"
int maxpgio = MAXPGIO;
int maxslp = MAXSLP;
int minfree = MINFREE;
int desfree = DESFREE;
/* In main.c since LOTSFREE is variable */
/* int lotsfree = LOTSFREE; */
int saferss = SAFERSS;
int slowscan = SLOWSCAN;
int fastscan = FASTSCAN;
int multprog = -1; /* so we don't count process 2 */
double avenrun[3]; /* load average, of runnable procs */
/*
* The main loop of the scheduling (swapping) process.
*
* The basic idea is:
* see if anyone wants to be swapped in;
* swap out processes until there is room;
* swap him in;
* repeat.
* If the paging rate is too high, or the average free memory
* is very low, then we do not consider swapping anyone in,
* but rather look for someone to swap out.
*
* The runout flag is set whenever someone is swapped out.
* Sched sleeps on it awaiting work.
*
* Sched sleeps on runin whenever it cannot find enough
* core (by swapping out or otherwise) to fit the
* selected swapped process. It is awakened when the
* core situation changes and in any case once per second.
*/
#define swappable(p) \
(((p)->p_flag&(SSYS|SLOCK|SULOCK|SLOAD|SPAGE|SKEEP|SWEXIT))==SLOAD)
/* insure non-zero */
#define nz(x) (x != 0 ? x : 1)
sched()
{
register struct proc *rp, *p, *inp;
register int outpri, inpri, rppri;
int smax;
/*
* Check if paging rate is too high, or average of
* free list very low and if so, adjust multiprogramming
* load by swapping someone out.
*
* Avoid glitches: don't swap out only process to do this,
* and don't swap based on paging rate if there is a reasonable
* amount of free memory.
*/
loop:
VOID spl6();
if (kmapwnt || (multprog > 1 && avefree < desfree &&
(rate.v_pgin + rate.v_pgout > maxpgio || avefree < minfree))) {
outpri = 10000;
p = 0;
} else {
/*
* Number of pages available and paging rate seem
* reasonable, consider increasing multiprogramming
* by swapping in process which has been out longest.
* If you went out with a lot of pages, then you are
* lower priority to come in... but are not brought in
* until there is a reasonable fraction of the memory
* you are expected to need available. The system will
* also protect memory for you to some extent in this
* case by computing the expected ``deficit'' (pages
* ``owed'' to you) and not giving them away via further
* swapins of process which want many pages.
*/
outpri = -20000;
for (rp = &proc[0]; rp < &proc[NPROC]; rp++) {
rppri = rp->p_time - (rp->p_nice-NZERO)*8;
if (rp->p_time < MAXSLP)
rppri -= rp->p_swrss / nz(maxpgio / 2);
if (rp->p_stat==SRUN && (rp->p_flag&SLOAD)==0 &&
rp->p_poip==0 &&
(rp->p_textp==0||rp->p_textp->x_poip==0) &&
rppri > outpri) {
p = rp;
outpri = rppri;
} else if ((rp->p_stat==SSLEEP||rp->p_stat==SSTOP) &&
(freemem < desfree || rp->p_rssize == 0) &&
rp->p_slptime > maxslp &&
(!rp->p_textp || (rp->p_textp->x_flag&XLOCK)==0) &&
swappable(rp)) {
/*
* We found a process which has been blocked
* in core for a long time, and memory is
* not as free as we would prefer.
* Swap it out to free its u. and page table
* pages, then start over. We do this here
* because we want to get rid of this guy
* even if noone wants to come in.
*/
rp->p_flag &= ~SLOAD;
VOID swapout(rp, rp->p_dsize, rp->p_ssize);
goto loop;
}
}
/*
* If there is no one there, wait.
*/
if (outpri == -20000) {
runout++;
sleep((caddr_t)&runout, PSWP);
goto loop;
}
VOID spl0();
/*
* If there are resources (kernel map, memory), swap p in.
* If the process was swapped out while it still had pages,
* don't bring it back unless there is a reasonable amount
* of memory for it to work with.
*/
if (freemem > imin(deficit, lotsfree) + imin(p->p_swrss / 2, 2 * maxpgio) ||
p->p_swrss < 2 * maxpgio && freemem > desfree) {
if (swapin(p))
goto loop;
}
}
/*
* Need resources (kernel map or memory), swap someone out.
* Select the person who has been sleeping longest
* at bad priority; if none, select the oldest.
*/
VOID spl6();
inp = p;
p = NULL;
smax = -1;
inpri = -1;
for (rp = &proc[0]; rp < &proc[NPROC]; rp++) {
if (rp->p_stat==SZOMB)
continue;
if (rp == inp)
continue;
if (!swappable(rp))
continue;
if (rp->p_textp && rp->p_textp->x_flag&XLOCK)
continue;
if ((rp->p_stat==SSLEEP&&rp->p_pri>=PZERO || rp->p_stat==SSTOP)
&& rp->p_slptime > maxslp) {
if (smax < rp->p_slptime) {
p = rp;
smax = rp->p_slptime;
}
} else if (smax<0 && (rp->p_stat==SRUN||rp->p_stat==SSLEEP)) {
rppri = rp->p_time+rp->p_nice-NZERO;
if (rp->p_time < maxslp)
rppri -= imin(rp->p_swrss / nz(maxpgio), maxslp / 2);
if (rppri > inpri) {
p = rp;
inpri = rppri;
}
}
}
/*
* Swap found user out if sleeping at bad pri for maxslp seconds,
* or if he has spent at least 5 seconds in core and
* the swapped-out process has spent at least 5 seconds out.
* Otherwise wait a bit and try again.
* (Note these are not really ``times'' but priorities.
*/
if (smax>=0 || (outpri>=5 && inpri>=5)) {
p->p_flag &= ~SLOAD;
VOID swapout(p, p->p_dsize, p->p_ssize);
goto loop;
}
VOID spl6();
runin++;
sleep((caddr_t)&runin, PSWP);
goto loop;
}
#define vave(field, time) \
ave(rate.field, cnt.field, time); sum.field += cnt.field; cnt.field = 0
vmmeter()
{
register int scanrate;
register int vavail;
deficit -= imin(deficit, imax(deficit / 10, maxpgio / 2));
ave(avefree, freemem, 5);
/* v_pgin is maintained by clock.c */
vave(v_pgout, 5);
vave(v_intrans, 5);
vave(v_pgrec, 5);
vave(v_exfod, 5);
vave(v_zfod, 5);
vave(v_vrfod, 5);
vave(v_nexfod, 5);
vave(v_nzfod, 5);
vave(v_nvrfod, 5);
vave(v_pgfrec, 5);
vave(v_faults, 5);
vave(v_scan, 5);
vave(v_rev, 5);
vave(v_dfree, 5);
vave(v_swtch, 5);
if (time % 5 == 0)
vmtotal();
if (time % 10 == 0) {
vave(v_swpin, 2);
vave(v_swpout, 2);
}
if (avefree < minfree && runout || proc[0].p_slptime > 5) {
runout = 0;
runin = 0;
wakeup((caddr_t)&runin);
wakeup((caddr_t)&runout);
}
/*
* Compute new rate for clock; if
* nonzero, restart clock.
* Rate ranges linearly from one rev per
* slowscan seconds when there is lotsfree memory
* available to one rev per fastscan seconds when
* there is no memory available.
*/
nscan = desscan = 0;
vavail = freemem - deficit;
if (freemem >= lotsfree)
return;
scanrate = (slowscan * vavail + fastscan * (lotsfree - vavail)) / nz(lotsfree);
desscan = LOOPSIZ / nz(scanrate);
wakeup((caddr_t)&proc[2]);
}
vmtotal()
{
register struct proc *p;
register struct text *xp;
int nrun = 0;
total.t_vmtxt = 0;
total.t_avmtxt = 0;
total.t_rmtxt = 0;
total.t_armtxt = 0;
for (xp = &text[0]; xp < &text[NTEXT]; xp++)
if (xp->x_iptr) {
total.t_vmtxt += xp->x_size;
total.t_rmtxt += xp->x_rssize;
for (p = xp->x_caddr; p; p = p->p_xlink)
switch (p->p_stat) {
case SSTOP:
case SSLEEP:
if (p->p_slptime >= maxslp)
continue;
/* fall into... */
case SRUN:
case SIDL:
total.t_avmtxt += xp->x_size;
total.t_armtxt += xp->x_rssize;
goto next;
}
next:
;
}
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_pw = 0;
total.t_sl = 0;
total.t_sw = 0;
for (p = &proc[0]; p < &proc[NPROC]; p++) {
if (p->p_flag & SSYS)
continue;
if (p->p_stat) {
total.t_vm += p->p_dsize + p->p_ssize;
total.t_rm += p->p_rssize;
switch (p->p_stat) {
case SSLEEP:
case SSTOP:
if (p->p_pri < PZERO)
nrun++;
if (p->p_flag & SPAGE)
total.t_pw++;
else 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;
break;
case SRUN:
case SIDL:
nrun++;
if (p->p_flag & SLOAD)
total.t_rq++;
else
total.t_sw++;
active:
total.t_avm += p->p_dsize + p->p_ssize;
total.t_arm += p->p_rssize;
break;
}
}
}
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;
loadav(avenrun, nrun);
}
/*
* 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]);
}