xref: /csrg-svn/sys/vm/vm_meter.c (revision 43)
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