1*43Sbill /* vm_meter.c 3.1 10/14/12 */ 2*43Sbill 3*43Sbill #include "../h/param.h" 4*43Sbill #include "../h/systm.h" 5*43Sbill #include "../h/seg.h" 6*43Sbill #include "../h/dir.h" 7*43Sbill #include "../h/user.h" 8*43Sbill #include "../h/proc.h" 9*43Sbill #include "../h/text.h" 10*43Sbill #include "../h/vm.h" 11*43Sbill #include "../h/cmap.h" 12*43Sbill 13*43Sbill int maxpgio = MAXPGIO; 14*43Sbill int maxslp = MAXSLP; 15*43Sbill int minfree = MINFREE; 16*43Sbill int desfree = DESFREE; 17*43Sbill int lotsfree = 0; /* set to LOTSFREE in main unless adbed */ 18*43Sbill int saferss = SAFERSS; 19*43Sbill int slowscan = SLOWSCAN; 20*43Sbill int fastscan = FASTSCAN; 21*43Sbill int klin = KLIN; 22*43Sbill int klout = KLOUT; 23*43Sbill int multprog = -1; /* so we don't count process 2 */ 24*43Sbill 25*43Sbill double avenrun[3]; /* load average, of runnable procs */ 26*43Sbill 27*43Sbill /* 28*43Sbill * The main loop of the scheduling (swapping) process. 29*43Sbill * 30*43Sbill * The basic idea is: 31*43Sbill * see if anyone wants to be swapped in; 32*43Sbill * swap out processes until there is room; 33*43Sbill * swap him in; 34*43Sbill * repeat. 35*43Sbill * If the paging rate is too high, or the average free memory 36*43Sbill * is very low, then we do not consider swapping anyone in, 37*43Sbill * but rather look for someone to swap out. 38*43Sbill * 39*43Sbill * The runout flag is set whenever someone is swapped out. 40*43Sbill * Sched sleeps on it awaiting work. 41*43Sbill * 42*43Sbill * Sched sleeps on runin whenever it cannot find enough 43*43Sbill * core (by swapping out or otherwise) to fit the 44*43Sbill * selected swapped process. It is awakened when the 45*43Sbill * core situation changes and in any case once per second. 46*43Sbill * 47*43Sbill * sched DOESN'T ACCOUNT FOR PAGE TABLE SIZE IN CALCULATIONS. 48*43Sbill */ 49*43Sbill 50*43Sbill #define swappable(p) \ 51*43Sbill (((p)->p_flag&(SSYS|SLOCK|SULOCK|SLOAD|SPAGE|SKEEP|SWEXIT|SPHYSIO))==SLOAD) 52*43Sbill 53*43Sbill /* insure non-zero */ 54*43Sbill #define nz(x) (x != 0 ? x : 1) 55*43Sbill 56*43Sbill #define NBIG 4 57*43Sbill #define MAXNBIG 10 58*43Sbill int nbig = NBIG; 59*43Sbill 60*43Sbill struct bigp { 61*43Sbill struct proc *bp_proc; 62*43Sbill int bp_pri; 63*43Sbill struct bigp *bp_link; 64*43Sbill } bigp[MAXNBIG], bplist; 65*43Sbill 66*43Sbill sched() 67*43Sbill { 68*43Sbill register struct proc *rp, *p, *inp; 69*43Sbill int outpri, inpri, rppri; 70*43Sbill int sleeper, desparate, deservin, needs, divisor; 71*43Sbill register struct bigp *bp, *nbp; 72*43Sbill int biggot, gives; 73*43Sbill 74*43Sbill /* 75*43Sbill * Check if paging rate is too high, or average of 76*43Sbill * free list very low and if so, adjust multiprogramming 77*43Sbill * load by swapping someone out. 78*43Sbill * 79*43Sbill * Avoid glitches: don't hard swap the only process, 80*43Sbill * and don't swap based on paging rate if there is a reasonable 81*43Sbill * amount of free memory. 82*43Sbill */ 83*43Sbill loop: 84*43Sbill VOID spl6(); 85*43Sbill deservin = 0; 86*43Sbill sleeper = 0; 87*43Sbill p = 0; 88*43Sbill if (kmapwnt || (multprog > 1 && avefree < desfree && 89*43Sbill (rate.v_pgin + rate.v_pgout > maxpgio || avefree < minfree))) { 90*43Sbill desparate = 1; 91*43Sbill goto hardswap; 92*43Sbill } 93*43Sbill desparate = 0; 94*43Sbill /* 95*43Sbill * Not desparate for core, 96*43Sbill * look for someone who deserves to be brought in. 97*43Sbill */ 98*43Sbill outpri = -20000; 99*43Sbill for (rp = &proc[0]; rp < &proc[NPROC]; rp++) switch(rp->p_stat) { 100*43Sbill 101*43Sbill case SRUN: 102*43Sbill if ((rp->p_flag&SLOAD) == 0) { 103*43Sbill rppri = rp->p_time - rp->p_swrss / nz((maxpgio/2) * CLSIZE) + rp->p_slptime 104*43Sbill - (rp->p_nice-NZERO)*8; 105*43Sbill if (rppri > outpri) { 106*43Sbill if (rp->p_poip) 107*43Sbill continue; 108*43Sbill if (rp->p_textp && rp->p_textp->x_poip) 109*43Sbill continue; 110*43Sbill p = rp; 111*43Sbill outpri = rppri; 112*43Sbill } 113*43Sbill } 114*43Sbill continue; 115*43Sbill 116*43Sbill case SSLEEP: 117*43Sbill case SSTOP: 118*43Sbill if ((freemem < desfree || rp->p_rssize == 0) && 119*43Sbill rp->p_slptime > maxslp && 120*43Sbill (!rp->p_textp || (rp->p_textp->x_flag&XLOCK)==0) && 121*43Sbill swappable(rp)) { 122*43Sbill /* 123*43Sbill * Kick out deadwood. 124*43Sbill * FOLLOWING 3 LINES MUST BE AT spl6(). 125*43Sbill */ 126*43Sbill rp->p_flag &= ~SLOAD; 127*43Sbill if (rp->p_stat == SRUN) 128*43Sbill remrq(rp); 129*43Sbill VOID swapout(rp, rp->p_dsize, rp->p_ssize); 130*43Sbill goto loop; 131*43Sbill } 132*43Sbill continue; 133*43Sbill } 134*43Sbill 135*43Sbill /* 136*43Sbill * No one wants in, so nothing to do. 137*43Sbill */ 138*43Sbill if (outpri == -20000) { 139*43Sbill runout++; 140*43Sbill sleep((caddr_t)&runout, PSWP); 141*43Sbill goto loop; 142*43Sbill } 143*43Sbill VOID spl0(); 144*43Sbill /* 145*43Sbill * Decide how deserving this guy is. If he is deserving 146*43Sbill * we will be willing to work harder to bring him in. 147*43Sbill * Needs is an estimate of how much core he will need. 148*43Sbill * If he has been out for a while, then we will 149*43Sbill * bring him in with 1/2 the core he will need, otherwise 150*43Sbill * we are conservative. 151*43Sbill */ 152*43Sbill deservin = 0; 153*43Sbill divisor = 1; 154*43Sbill if (outpri > maxslp/2) { 155*43Sbill deservin = 1; 156*43Sbill divisor = 2; 157*43Sbill } 158*43Sbill needs = p->p_swrss; 159*43Sbill if (p->p_textp && p->p_textp->x_ccount == 0) 160*43Sbill needs += p->p_textp->x_swrss; 161*43Sbill if (freemem - deficit > needs / divisor) { 162*43Sbill deficit += needs; 163*43Sbill if (swapin(p)) 164*43Sbill goto loop; 165*43Sbill deficit -= imin(needs, deficit); 166*43Sbill } 167*43Sbill 168*43Sbill hardswap: 169*43Sbill /* 170*43Sbill * Need resources (kernel map or memory), swap someone out. 171*43Sbill * Select the nbig largest jobs, then the oldest of these 172*43Sbill * is ``most likely to get booted.'' 173*43Sbill */ 174*43Sbill VOID spl6(); 175*43Sbill inp = p; 176*43Sbill sleeper = 0; 177*43Sbill if (nbig > MAXNBIG) 178*43Sbill nbig = MAXNBIG; 179*43Sbill if (nbig < 1) 180*43Sbill nbig = 1; 181*43Sbill biggot = 0; 182*43Sbill bplist.bp_link = 0; 183*43Sbill for (rp = &proc[0]; rp < &proc[NPROC]; rp++) { 184*43Sbill if (!swappable(rp)) 185*43Sbill continue; 186*43Sbill if (rp->p_stat==SZOMB) 187*43Sbill continue; 188*43Sbill if (rp == inp) 189*43Sbill continue; 190*43Sbill if (rp->p_textp && rp->p_textp->x_flag&XLOCK) 191*43Sbill continue; 192*43Sbill if (rp->p_slptime > maxslp && 193*43Sbill (rp->p_stat==SSLEEP&&rp->p_pri>=PZERO||rp->p_stat==SSTOP)) { 194*43Sbill if (sleeper < rp->p_slptime) { 195*43Sbill p = rp; 196*43Sbill sleeper = rp->p_slptime; 197*43Sbill } 198*43Sbill } else if (!sleeper && (rp->p_stat==SRUN||rp->p_stat==SSLEEP)) { 199*43Sbill rppri = rp->p_rssize; 200*43Sbill if (rp->p_textp) 201*43Sbill rppri += rp->p_textp->x_rssize / rp->p_textp->x_ccount; 202*43Sbill if (biggot < nbig) 203*43Sbill nbp = &bigp[biggot++]; 204*43Sbill else { 205*43Sbill nbp = bplist.bp_link; 206*43Sbill if (nbp->bp_pri > rppri) 207*43Sbill continue; 208*43Sbill bplist.bp_link = nbp->bp_link; 209*43Sbill } 210*43Sbill for (bp = &bplist; bp->bp_link; bp = bp->bp_link) 211*43Sbill if (rppri < bp->bp_link->bp_pri) 212*43Sbill break; 213*43Sbill nbp->bp_link = bp->bp_link; 214*43Sbill bp->bp_link = nbp; 215*43Sbill nbp->bp_pri = rppri; 216*43Sbill nbp->bp_proc = rp; 217*43Sbill } 218*43Sbill } 219*43Sbill if (!sleeper) { 220*43Sbill p = NULL; 221*43Sbill inpri = -1000; 222*43Sbill for (bp = bplist.bp_link; bp; bp = bp->bp_link) { 223*43Sbill rp = bp->bp_proc; 224*43Sbill rppri = rp->p_time+rp->p_nice-NZERO; 225*43Sbill if (rppri >= inpri) { 226*43Sbill p = rp; 227*43Sbill inpri = rppri; 228*43Sbill } 229*43Sbill } 230*43Sbill } 231*43Sbill /* 232*43Sbill * If we found a long-time sleeper, or we are desparate and 233*43Sbill * found anyone to swap out, or if someone deserves to come 234*43Sbill * in and we didn't find a sleeper, but found someone who 235*43Sbill * has been in core for a reasonable length of time, then 236*43Sbill * we kick the poor luser out. 237*43Sbill */ 238*43Sbill if (sleeper || desparate && p || deservin && inpri > maxslp) { 239*43Sbill p->p_flag &= ~SLOAD; 240*43Sbill if (p->p_stat == SRUN) 241*43Sbill remrq(p); 242*43Sbill if (desparate) { 243*43Sbill /* 244*43Sbill * Want to give this space to the rest of 245*43Sbill * the processes in core so give them a chance 246*43Sbill * by increasing the deficit. 247*43Sbill */ 248*43Sbill gives = p->p_rssize; 249*43Sbill if (p->p_textp) 250*43Sbill gives += p->p_textp->x_rssize / p->p_textp->x_ccount; 251*43Sbill deficit += gives; 252*43Sbill } else 253*43Sbill gives = 0; /* someone else taketh away */ 254*43Sbill if (swapout(p, p->p_dsize, p->p_ssize) == 0) 255*43Sbill deficit -= imin(gives, deficit); 256*43Sbill goto loop; 257*43Sbill } 258*43Sbill /* 259*43Sbill * Want to swap someone in, but can't 260*43Sbill * so wait on runin. 261*43Sbill */ 262*43Sbill VOID spl6(); 263*43Sbill runin++; 264*43Sbill sleep((caddr_t)&runin, PSWP); 265*43Sbill goto loop; 266*43Sbill } 267*43Sbill 268*43Sbill vmmeter() 269*43Sbill { 270*43Sbill register unsigned *cp, *rp, *sp; 271*43Sbill 272*43Sbill deficit -= imin(deficit, imax(deficit / 10, maxpgio / 2)); 273*43Sbill ave(avefree, freemem, 5); 274*43Sbill /* v_pgin is maintained by clock.c */ 275*43Sbill cp = &cnt.v_first; rp = &rate.v_first; sp = &sum.v_first; 276*43Sbill while (cp <= &cnt.v_last) { 277*43Sbill ave(*rp, *cp, 5); 278*43Sbill *sp += *cp; 279*43Sbill *cp = 0; 280*43Sbill rp++, cp++, sp++; 281*43Sbill } 282*43Sbill if (time % 5 == 0) { 283*43Sbill vmtotal(); 284*43Sbill rate.v_swpin = cnt.v_swpin; 285*43Sbill sum.v_swpin += cnt.v_swpin; 286*43Sbill cnt.v_swpin = 0; 287*43Sbill rate.v_swpout = cnt.v_swpout; 288*43Sbill sum.v_swpout += cnt.v_swpout; 289*43Sbill cnt.v_swpout = 0; 290*43Sbill } 291*43Sbill if (avefree < minfree && runout || proc[0].p_slptime > maxslp/2) { 292*43Sbill runout = 0; 293*43Sbill runin = 0; 294*43Sbill wakeup((caddr_t)&runin); 295*43Sbill wakeup((caddr_t)&runout); 296*43Sbill } 297*43Sbill } 298*43Sbill 299*43Sbill vmpago() 300*43Sbill { 301*43Sbill register int vavail; 302*43Sbill register int scanrate; 303*43Sbill 304*43Sbill /* 305*43Sbill * Compute new rate for clock; if 306*43Sbill * nonzero, restart clock. 307*43Sbill * Rate ranges linearly from one rev per 308*43Sbill * slowscan seconds when there is lotsfree memory 309*43Sbill * available to one rev per fastscan seconds when 310*43Sbill * there is no memory available. 311*43Sbill */ 312*43Sbill nscan = desscan = 0; 313*43Sbill vavail = freemem - deficit; 314*43Sbill if (vavail < 0) 315*43Sbill vavail = 0; 316*43Sbill if (freemem >= lotsfree) 317*43Sbill return; 318*43Sbill scanrate = (slowscan * vavail + fastscan * (lotsfree - vavail)) / nz(lotsfree); 319*43Sbill desscan = LOOPSIZ / nz(scanrate); 320*43Sbill /* 321*43Sbill * DIVIDE BY 4 TO ACCOUNT FOR RUNNING 4* A SECOND (see clock.c) 322*43Sbill */ 323*43Sbill desscan /= 4; 324*43Sbill wakeup((caddr_t)&proc[2]); 325*43Sbill } 326*43Sbill 327*43Sbill vmtotal() 328*43Sbill { 329*43Sbill register struct proc *p; 330*43Sbill register struct text *xp; 331*43Sbill int nrun = 0; 332*43Sbill 333*43Sbill total.t_vmtxt = 0; 334*43Sbill total.t_avmtxt = 0; 335*43Sbill total.t_rmtxt = 0; 336*43Sbill total.t_armtxt = 0; 337*43Sbill for (xp = &text[0]; xp < &text[NTEXT]; xp++) 338*43Sbill if (xp->x_iptr) { 339*43Sbill total.t_vmtxt += xp->x_size; 340*43Sbill total.t_rmtxt += xp->x_rssize; 341*43Sbill for (p = xp->x_caddr; p; p = p->p_xlink) 342*43Sbill switch (p->p_stat) { 343*43Sbill 344*43Sbill case SSTOP: 345*43Sbill case SSLEEP: 346*43Sbill if (p->p_slptime >= maxslp) 347*43Sbill continue; 348*43Sbill /* fall into... */ 349*43Sbill 350*43Sbill case SRUN: 351*43Sbill case SIDL: 352*43Sbill total.t_avmtxt += xp->x_size; 353*43Sbill total.t_armtxt += xp->x_rssize; 354*43Sbill goto next; 355*43Sbill } 356*43Sbill next: 357*43Sbill ; 358*43Sbill } 359*43Sbill total.t_vm = 0; 360*43Sbill total.t_avm = 0; 361*43Sbill total.t_rm = 0; 362*43Sbill total.t_arm = 0; 363*43Sbill total.t_rq = 0; 364*43Sbill total.t_dw = 0; 365*43Sbill total.t_pw = 0; 366*43Sbill total.t_sl = 0; 367*43Sbill total.t_sw = 0; 368*43Sbill for (p = &proc[0]; p < &proc[NPROC]; p++) { 369*43Sbill if (p->p_flag & SSYS) 370*43Sbill continue; 371*43Sbill if (p->p_stat) { 372*43Sbill total.t_vm += p->p_dsize + p->p_ssize; 373*43Sbill total.t_rm += p->p_rssize; 374*43Sbill switch (p->p_stat) { 375*43Sbill 376*43Sbill case SSLEEP: 377*43Sbill case SSTOP: 378*43Sbill if (p->p_pri < PZERO) 379*43Sbill nrun++; 380*43Sbill if (p->p_flag & SPAGE) 381*43Sbill total.t_pw++; 382*43Sbill else if (p->p_flag & SLOAD) { 383*43Sbill if (p->p_pri < PZERO) 384*43Sbill total.t_dw++; 385*43Sbill else if (p->p_slptime < maxslp) 386*43Sbill total.t_sl++; 387*43Sbill } else if (p->p_slptime < maxslp) 388*43Sbill total.t_sw++; 389*43Sbill if (p->p_slptime < maxslp) 390*43Sbill goto active; 391*43Sbill break; 392*43Sbill 393*43Sbill case SRUN: 394*43Sbill case SIDL: 395*43Sbill nrun++; 396*43Sbill if (p->p_flag & SLOAD) 397*43Sbill total.t_rq++; 398*43Sbill else 399*43Sbill total.t_sw++; 400*43Sbill active: 401*43Sbill total.t_avm += p->p_dsize + p->p_ssize; 402*43Sbill total.t_arm += p->p_rssize; 403*43Sbill break; 404*43Sbill } 405*43Sbill } 406*43Sbill } 407*43Sbill total.t_vm += total.t_vmtxt; 408*43Sbill total.t_avm += total.t_avmtxt; 409*43Sbill total.t_rm += total.t_rmtxt; 410*43Sbill total.t_arm += total.t_armtxt; 411*43Sbill total.t_free = avefree; 412*43Sbill loadav(avenrun, nrun); 413*43Sbill } 414*43Sbill 415*43Sbill /* 416*43Sbill * Constants for averages over 1, 5, and 15 minutes 417*43Sbill * when sampling at 5 second intervals. 418*43Sbill */ 419*43Sbill double cexp[3] = { 420*43Sbill 0.9200444146293232, /* exp(-1/12) */ 421*43Sbill 0.9834714538216174, /* exp(-1/60) */ 422*43Sbill 0.9944598480048967, /* exp(-1/180) */ 423*43Sbill }; 424*43Sbill 425*43Sbill /* 426*43Sbill * Compute a tenex style load average of a quantity on 427*43Sbill * 1, 5 and 15 minute intervals. 428*43Sbill */ 429*43Sbill loadav(avg, n) 430*43Sbill register double *avg; 431*43Sbill int n; 432*43Sbill { 433*43Sbill register int i; 434*43Sbill 435*43Sbill for (i = 0; i < 3; i++) 436*43Sbill avg[i] = cexp[i] * avg[i] + n * (1.0 - cexp[i]); 437*43Sbill } 438