1 /* vm_meter.c 4.17 82/10/31 */ 2 3 #include "../h/param.h" 4 #include "../h/systm.h" 5 #include "../h/seg.h" 6 #include "../h/dir.h" 7 #include "../h/user.h" 8 #include "../h/proc.h" 9 #include "../h/text.h" 10 #include "../h/vm.h" 11 #include "../h/cmap.h" 12 #include "../h/kernel.h" 13 14 int maxslp = MAXSLP; 15 int saferss = SAFERSS; 16 17 /* 18 * The following parameters control operation of the page replacement 19 * algorithm. They are initialized to 0, and then computed at boot time 20 * based on the size of the system. If they are patched non-zero in 21 * a loaded vmunix they are left alone and may thus be changed per system 22 * using adb on the loaded system. 23 */ 24 int maxpgio = 0; 25 int minfree = 0; 26 int desfree = 0; 27 int lotsfree = 0; 28 int slowscan = 0; 29 int fastscan = 0; 30 int klin = KLIN; 31 int klseql = KLSEQL; 32 int klsdist = KLSDIST; 33 int kltxt = KLTXT; 34 int klout = KLOUT; 35 int multprog = -1; /* so we don't count process 2 */ 36 37 double avenrun[3]; /* load average, of runnable procs */ 38 39 /* 40 * Setup the paging constants for the clock algorithm. 41 * Called after the system is initialized and the amount of memory 42 * and number of paging devices is known. 43 */ 44 setupclock() 45 { 46 47 /* 48 * Setup thresholds for paging: 49 * lotsfree is threshold where paging daemon turns on 50 * desfree is amount of memory desired free. if less 51 * than this for extended period, do swapping 52 * minfree is minimal amount of free memory which is 53 * tolerable. 54 * 55 * Strategy of 4/22/81: 56 * lotsfree is 1/4 of memory free. 57 * desfree is 200k bytes, but at most 1/8 of memory 58 * minfree is 64k bytes, but at most 1/2 of desfree 59 */ 60 if (lotsfree == 0) 61 lotsfree = LOOPPAGES / 4; 62 if (desfree == 0) { 63 desfree = (200*1024) / NBPG; 64 if (desfree > LOOPPAGES / 8) 65 desfree = LOOPPAGES / 8; 66 } 67 if (minfree == 0) { 68 minfree = (64*1024) / NBPG; 69 if (minfree > desfree/2) 70 minfree = desfree / 2; 71 } 72 73 /* 74 * Maxpgio thresholds how much paging is acceptable. 75 * This figures that 2/3 busy on an arm is all that is 76 * tolerable for paging. We assume one operation per disk rev. 77 */ 78 if (maxpgio == 0) 79 maxpgio = (DISKRPM * 2) / 3; 80 81 /* 82 * Clock to scan using max of ~~10% of processor time for sampling, 83 * this estimated to allow maximum of 200 samples per second. 84 * This yields a ``fastscan'' of roughly (with CLSIZE=2): 85 * <=1m 2m 3m 4m 8m 86 * 5s 10s 15s 20s 40s 87 */ 88 if (nswdev == 1 && physmem*NBPG > 2*1024*(1024-16)) 89 printf("WARNING: should run interleaved swap with >= 2Mb\n"); 90 if (fastscan == 0) 91 fastscan = (LOOPPAGES/CLSIZE) / 200; 92 if (fastscan < 5) 93 fastscan = 5; 94 if (nswdev >= 2) 95 maxpgio = (maxpgio * 3) / 2; 96 97 /* 98 * Set slow scan time to 1/2 the fast scan time. 99 */ 100 if (slowscan == 0) 101 slowscan = 2 * fastscan; 102 #ifdef notdef 103 printf("slowscan %d, fastscan %d, maxpgio %d\n", 104 slowscan, fastscan, maxpgio); 105 printf("lotsfree %d, desfree %d, minfree %d\n", 106 lotsfree, desfree, minfree); 107 #endif 108 } 109 110 /* 111 * The main loop of the scheduling (swapping) process. 112 * 113 * The basic idea is: 114 * see if anyone wants to be swapped in; 115 * swap out processes until there is room; 116 * swap him in; 117 * repeat. 118 * If the paging rate is too high, or the average free memory 119 * is very low, then we do not consider swapping anyone in, 120 * but rather look for someone to swap out. 121 * 122 * The runout flag is set whenever someone is swapped out. 123 * Sched sleeps on it awaiting work. 124 * 125 * Sched sleeps on runin whenever it cannot find enough 126 * core (by swapping out or otherwise) to fit the 127 * selected swapped process. It is awakened when the 128 * core situation changes and in any case once per second. 129 * 130 * sched DOESN'T ACCOUNT FOR PAGE TABLE SIZE IN CALCULATIONS. 131 */ 132 133 #define swappable(p) \ 134 (((p)->p_flag&(SSYS|SLOCK|SULOCK|SLOAD|SPAGE|SKEEP|SWEXIT|SPHYSIO))==SLOAD) 135 136 /* insure non-zero */ 137 #define nz(x) (x != 0 ? x : 1) 138 139 #define NBIG 4 140 #define MAXNBIG 10 141 int nbig = NBIG; 142 143 struct bigp { 144 struct proc *bp_proc; 145 int bp_pri; 146 struct bigp *bp_link; 147 } bigp[MAXNBIG], bplist; 148 149 sched() 150 { 151 register struct proc *rp, *p, *inp; 152 int outpri, inpri, rppri; 153 int sleeper, desperate, deservin, needs, divisor; 154 register struct bigp *bp, *nbp; 155 int biggot, gives; 156 157 loop: 158 wantin = 0; 159 deservin = 0; 160 sleeper = 0; 161 p = 0; 162 /* 163 * See if paging system is overloaded; if so swap someone out. 164 * Conditions for hard outswap are: 165 * if need kernel map (mix it up). 166 * or 167 * 1. if there are at least 2 runnable processes (on the average) 168 * and 2. the paging rate is excessive or memory is now VERY low. 169 * and 3. the short (5-second) and longer (30-second) average 170 * memory is less than desirable. 171 */ 172 if ( 173 #ifdef NOPAGING 174 freemem == 0 || 175 #endif 176 kmapwnt || (avenrun[0] >= 2 && imax(avefree, avefree30) < desfree && 177 (rate.v_pgin + rate.v_pgout > maxpgio || avefree < minfree))) { 178 desperate = 1; 179 goto hardswap; 180 } 181 desperate = 0; 182 /* 183 * Not desperate for core, 184 * look for someone who deserves to be brought in. 185 */ 186 outpri = -20000; 187 for (rp = proc; rp < procNPROC; rp++) switch(rp->p_stat) { 188 189 case SRUN: 190 if ((rp->p_flag&SLOAD) == 0) { 191 rppri = rp->p_time - 192 rp->p_swrss / nz((maxpgio/2) * (klin * CLSIZE)) + 193 rp->p_slptime - (rp->p_nice-NZERO)*8; 194 if (rppri > outpri) { 195 if (rp->p_poip) 196 continue; 197 if (rp->p_textp && rp->p_textp->x_poip) 198 continue; 199 p = rp; 200 outpri = rppri; 201 } 202 } 203 continue; 204 205 case SSLEEP: 206 case SSTOP: 207 if ((freemem < desfree || rp->p_rssize == 0) && 208 rp->p_slptime > maxslp && 209 (!rp->p_textp || (rp->p_textp->x_flag&XLOCK)==0) && 210 swappable(rp)) { 211 /* 212 * Kick out deadwood. 213 */ 214 (void) spl6(); 215 rp->p_flag &= ~SLOAD; 216 if (rp->p_stat == SRUN) 217 remrq(rp); 218 (void) spl0(); 219 (void) swapout(rp, rp->p_dsize, rp->p_ssize); 220 goto loop; 221 } 222 continue; 223 } 224 225 /* 226 * No one wants in, so nothing to do. 227 */ 228 if (outpri == -20000) { 229 (void) spl6(); 230 if (wantin) { 231 wantin = 0; 232 sleep((caddr_t)&lbolt, PSWP); 233 } else { 234 runout++; 235 sleep((caddr_t)&runout, PSWP); 236 } 237 (void) spl0(); 238 goto loop; 239 } 240 /* 241 * Decide how deserving this guy is. If he is deserving 242 * we will be willing to work harder to bring him in. 243 * Needs is an estimate of how much core he will need. 244 * If he has been out for a while, then we will 245 * bring him in with 1/2 the core he will need, otherwise 246 * we are conservative. 247 */ 248 deservin = 0; 249 divisor = 1; 250 if (outpri > maxslp/2) { 251 deservin = 1; 252 #ifdef NOPAGING 253 divisor = 1; 254 #else 255 divisor = 2; 256 #endif 257 } 258 needs = p->p_swrss; 259 if (p->p_textp && p->p_textp->x_ccount == 0) 260 needs += p->p_textp->x_swrss; 261 needs = imin(needs, lotsfree); 262 if (freemem - deficit > needs / divisor) { 263 deficit += needs; 264 if (swapin(p)) 265 goto loop; 266 deficit -= imin(needs, deficit); 267 } 268 269 hardswap: 270 /* 271 * Need resources (kernel map or memory), swap someone out. 272 * Select the nbig largest jobs, then the oldest of these 273 * is ``most likely to get booted.'' 274 */ 275 inp = p; 276 sleeper = 0; 277 if (nbig > MAXNBIG) 278 nbig = MAXNBIG; 279 if (nbig < 1) 280 nbig = 1; 281 biggot = 0; 282 bplist.bp_link = 0; 283 for (rp = proc; rp < procNPROC; rp++) { 284 if (!swappable(rp)) 285 continue; 286 if (rp->p_stat==SZOMB) 287 continue; 288 if (rp == inp) 289 continue; 290 if (rp->p_textp && rp->p_textp->x_flag&XLOCK) 291 continue; 292 if (rp->p_slptime > maxslp && 293 (rp->p_stat==SSLEEP&&rp->p_pri>PZERO||rp->p_stat==SSTOP)) { 294 if (sleeper < rp->p_slptime) { 295 p = rp; 296 sleeper = rp->p_slptime; 297 } 298 } else if (!sleeper && (rp->p_stat==SRUN||rp->p_stat==SSLEEP)) { 299 rppri = rp->p_rssize; 300 if (rp->p_textp) 301 rppri += rp->p_textp->x_rssize/rp->p_textp->x_ccount; 302 if (biggot < nbig) 303 nbp = &bigp[biggot++]; 304 else { 305 nbp = bplist.bp_link; 306 if (nbp->bp_pri > rppri) 307 continue; 308 bplist.bp_link = nbp->bp_link; 309 } 310 for (bp = &bplist; bp->bp_link; bp = bp->bp_link) 311 if (rppri < bp->bp_link->bp_pri) 312 break; 313 nbp->bp_link = bp->bp_link; 314 bp->bp_link = nbp; 315 nbp->bp_pri = rppri; 316 nbp->bp_proc = rp; 317 } 318 } 319 if (!sleeper) { 320 p = NULL; 321 inpri = -1000; 322 for (bp = bplist.bp_link; bp; bp = bp->bp_link) { 323 rp = bp->bp_proc; 324 rppri = rp->p_time+rp->p_nice-NZERO; 325 if (rppri >= inpri) { 326 p = rp; 327 inpri = rppri; 328 } 329 } 330 } 331 /* 332 * If we found a long-time sleeper, or we are desperate and 333 * found anyone to swap out, or if someone deserves to come 334 * in and we didn't find a sleeper, but found someone who 335 * has been in core for a reasonable length of time, then 336 * we kick the poor luser out. 337 */ 338 if (sleeper || desperate && p || deservin && inpri > maxslp) { 339 (void) spl6(); 340 p->p_flag &= ~SLOAD; 341 if (p->p_stat == SRUN) 342 remrq(p); 343 (void) spl0(); 344 if (desperate) { 345 /* 346 * Want to give this space to the rest of 347 * the processes in core so give them a chance 348 * by increasing the deficit. 349 */ 350 gives = p->p_rssize; 351 if (p->p_textp) 352 gives += p->p_textp->x_rssize / p->p_textp->x_ccount; 353 gives = imin(gives, lotsfree); 354 deficit += gives; 355 } else 356 gives = 0; /* someone else taketh away */ 357 if (swapout(p, p->p_dsize, p->p_ssize) == 0) 358 deficit -= imin(gives, deficit); 359 goto loop; 360 } 361 /* 362 * Want to swap someone in, but can't 363 * so wait on runin. 364 */ 365 (void) spl6(); 366 runin++; 367 sleep((caddr_t)&runin, PSWP); 368 (void) spl0(); 369 goto loop; 370 } 371 372 vmmeter() 373 { 374 register unsigned *cp, *rp, *sp; 375 376 deficit -= imin(deficit, 377 imax(deficit / 10, ((klin * CLSIZE) / 2) * maxpgio / 2)); 378 ave(avefree, freemem, 5); 379 ave(avefree30, freemem, 30); 380 /* v_pgin is maintained by clock.c */ 381 cp = &cnt.v_first; rp = &rate.v_first; sp = &sum.v_first; 382 while (cp <= &cnt.v_last) { 383 ave(*rp, *cp, 5); 384 *sp += *cp; 385 *cp = 0; 386 rp++, cp++, sp++; 387 } 388 if (time.tv_sec % 5 == 0) { 389 vmtotal(); 390 rate.v_swpin = cnt.v_swpin; 391 sum.v_swpin += cnt.v_swpin; 392 cnt.v_swpin = 0; 393 rate.v_swpout = cnt.v_swpout; 394 sum.v_swpout += cnt.v_swpout; 395 cnt.v_swpout = 0; 396 } 397 if (avefree < minfree && runout || proc[0].p_slptime > maxslp/2) { 398 runout = 0; 399 runin = 0; 400 wakeup((caddr_t)&runin); 401 wakeup((caddr_t)&runout); 402 } 403 } 404 405 #define RATETOSCHEDPAGING 4 /* hz that is */ 406 407 /* 408 * Schedule rate for paging. 409 * Rate is linear interpolation between 410 * slowscan with lotsfree and fastscan when out of memory. 411 */ 412 schedpaging() 413 { 414 register int vavail, scanrate; 415 416 nscan = desscan = 0; 417 vavail = freemem - deficit; 418 if (vavail < 0) 419 vavail = 0; 420 if (freemem >= lotsfree) 421 return; 422 scanrate = (slowscan * vavail + fastscan * (lotsfree - vavail)) / nz(lotsfree); 423 desscan = ((LOOPPAGES / CLSIZE) / nz(scanrate)) / RATETOSCHEDPAGING; 424 wakeup((caddr_t)&proc[2]); 425 timeout(schedpaging, (caddr_t)0, hz / RATETOSCHEDPAGING); 426 } 427 428 vmtotal() 429 { 430 register struct proc *p; 431 register struct text *xp; 432 int nrun = 0; 433 434 total.t_vmtxt = 0; 435 total.t_avmtxt = 0; 436 total.t_rmtxt = 0; 437 total.t_armtxt = 0; 438 for (xp = text; xp < textNTEXT; xp++) 439 if (xp->x_iptr) { 440 total.t_vmtxt += xp->x_size; 441 total.t_rmtxt += xp->x_rssize; 442 for (p = xp->x_caddr; p; p = p->p_xlink) 443 switch (p->p_stat) { 444 445 case SSTOP: 446 case SSLEEP: 447 if (p->p_slptime >= maxslp) 448 continue; 449 /* fall into... */ 450 451 case SRUN: 452 case SIDL: 453 total.t_avmtxt += xp->x_size; 454 total.t_armtxt += xp->x_rssize; 455 goto next; 456 } 457 next: 458 ; 459 } 460 total.t_vm = 0; 461 total.t_avm = 0; 462 total.t_rm = 0; 463 total.t_arm = 0; 464 total.t_rq = 0; 465 total.t_dw = 0; 466 total.t_pw = 0; 467 total.t_sl = 0; 468 total.t_sw = 0; 469 for (p = proc; p < procNPROC; p++) { 470 if (p->p_flag & SSYS) 471 continue; 472 if (p->p_stat) { 473 total.t_vm += p->p_dsize + p->p_ssize; 474 total.t_rm += p->p_rssize; 475 switch (p->p_stat) { 476 477 case SSLEEP: 478 case SSTOP: 479 if (p->p_pri <= PZERO) 480 nrun++; 481 if (p->p_flag & SPAGE) 482 total.t_pw++; 483 else if (p->p_flag & SLOAD) { 484 if (p->p_pri <= PZERO) 485 total.t_dw++; 486 else if (p->p_slptime < maxslp) 487 total.t_sl++; 488 } else if (p->p_slptime < maxslp) 489 total.t_sw++; 490 if (p->p_slptime < maxslp) 491 goto active; 492 break; 493 494 case SRUN: 495 case SIDL: 496 nrun++; 497 if (p->p_flag & SLOAD) 498 total.t_rq++; 499 else 500 total.t_sw++; 501 active: 502 total.t_avm += p->p_dsize + p->p_ssize; 503 total.t_arm += p->p_rssize; 504 break; 505 } 506 } 507 } 508 total.t_vm += total.t_vmtxt; 509 total.t_avm += total.t_avmtxt; 510 total.t_rm += total.t_rmtxt; 511 total.t_arm += total.t_armtxt; 512 total.t_free = avefree; 513 loadav(avenrun, nrun); 514 } 515 516 /* 517 * Constants for averages over 1, 5, and 15 minutes 518 * when sampling at 5 second intervals. 519 */ 520 double cexp[3] = { 521 0.9200444146293232, /* exp(-1/12) */ 522 0.9834714538216174, /* exp(-1/60) */ 523 0.9944598480048967, /* exp(-1/180) */ 524 }; 525 526 /* 527 * Compute a tenex style load average of a quantity on 528 * 1, 5 and 15 minute intervals. 529 */ 530 loadav(avg, n) 531 register double *avg; 532 int n; 533 { 534 register int i; 535 536 for (i = 0; i < 3; i++) 537 avg[i] = cexp[i] * avg[i] + n * (1.0 - cexp[i]); 538 } 539