xref: /netbsd-src/sys/uvm/uvm_pdpolicy_clock.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: uvm_pdpolicy_clock.c,v 1.12 2008/06/04 12:41:40 ad Exp $	*/
2 /*	NetBSD: uvm_pdaemon.c,v 1.72 2006/01/05 10:47:33 yamt Exp $	*/
3 
4 /*
5  * Copyright (c) 1997 Charles D. Cranor and Washington University.
6  * Copyright (c) 1991, 1993, The Regents of the University of California.
7  *
8  * All rights reserved.
9  *
10  * This code is derived from software contributed to Berkeley by
11  * The Mach Operating System project at Carnegie-Mellon University.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *	This product includes software developed by Charles D. Cranor,
24  *      Washington University, the University of California, Berkeley and
25  *      its contributors.
26  * 4. Neither the name of the University nor the names of its contributors
27  *    may be used to endorse or promote products derived from this software
28  *    without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
31  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
34  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40  * SUCH DAMAGE.
41  *
42  *	@(#)vm_pageout.c        8.5 (Berkeley) 2/14/94
43  * from: Id: uvm_pdaemon.c,v 1.1.2.32 1998/02/06 05:26:30 chs Exp
44  *
45  *
46  * Copyright (c) 1987, 1990 Carnegie-Mellon University.
47  * All rights reserved.
48  *
49  * Permission to use, copy, modify and distribute this software and
50  * its documentation is hereby granted, provided that both the copyright
51  * notice and this permission notice appear in all copies of the
52  * software, derivative works or modified versions, and any portions
53  * thereof, and that both notices appear in supporting documentation.
54  *
55  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
56  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
57  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
58  *
59  * Carnegie Mellon requests users of this software to return to
60  *
61  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
62  *  School of Computer Science
63  *  Carnegie Mellon University
64  *  Pittsburgh PA 15213-3890
65  *
66  * any improvements or extensions that they make and grant Carnegie the
67  * rights to redistribute these changes.
68  */
69 
70 #if defined(PDSIM)
71 
72 #include "pdsim.h"
73 
74 #else /* defined(PDSIM) */
75 
76 #include <sys/cdefs.h>
77 __KERNEL_RCSID(0, "$NetBSD: uvm_pdpolicy_clock.c,v 1.12 2008/06/04 12:41:40 ad Exp $");
78 
79 #include <sys/param.h>
80 #include <sys/proc.h>
81 #include <sys/systm.h>
82 #include <sys/kernel.h>
83 
84 #include <uvm/uvm.h>
85 #include <uvm/uvm_pdpolicy.h>
86 #include <uvm/uvm_pdpolicy_impl.h>
87 
88 #endif /* defined(PDSIM) */
89 
90 #define PQ_INACTIVE	PQ_PRIVATE1	/* page is in inactive list */
91 #define PQ_ACTIVE	PQ_PRIVATE2	/* page is in active list */
92 
93 #if !defined(CLOCK_INACTIVEPCT)
94 #define	CLOCK_INACTIVEPCT	33
95 #endif /* !defined(CLOCK_INACTIVEPCT) */
96 
97 struct uvmpdpol_globalstate {
98 	struct pglist s_activeq;	/* allocated pages, in use */
99 	struct pglist s_inactiveq;	/* pages between the clock hands */
100 	int s_active;
101 	int s_inactive;
102 	int s_inactarg;
103 	struct uvm_pctparam s_anonmin;
104 	struct uvm_pctparam s_filemin;
105 	struct uvm_pctparam s_execmin;
106 	struct uvm_pctparam s_anonmax;
107 	struct uvm_pctparam s_filemax;
108 	struct uvm_pctparam s_execmax;
109 	struct uvm_pctparam s_inactivepct;
110 };
111 
112 struct uvmpdpol_scanstate {
113 	bool ss_first;
114 	bool ss_anonreact, ss_filereact, ss_execreact;
115 	struct vm_page *ss_nextpg;
116 };
117 
118 static struct uvmpdpol_globalstate pdpol_state;
119 static struct uvmpdpol_scanstate pdpol_scanstate;
120 
121 PDPOL_EVCNT_DEFINE(reactexec)
122 PDPOL_EVCNT_DEFINE(reactfile)
123 PDPOL_EVCNT_DEFINE(reactanon)
124 
125 static void
126 clock_tune(void)
127 {
128 	struct uvmpdpol_globalstate *s = &pdpol_state;
129 
130 	s->s_inactarg = UVM_PCTPARAM_APPLY(&s->s_inactivepct,
131 	    s->s_active + s->s_inactive);
132 	if (s->s_inactarg <= uvmexp.freetarg) {
133 		s->s_inactarg = uvmexp.freetarg + 1;
134 	}
135 }
136 
137 void
138 uvmpdpol_scaninit(void)
139 {
140 	struct uvmpdpol_globalstate *s = &pdpol_state;
141 	struct uvmpdpol_scanstate *ss = &pdpol_scanstate;
142 	int t;
143 	bool anonunder, fileunder, execunder;
144 	bool anonover, fileover, execover;
145 	bool anonreact, filereact, execreact;
146 
147 	/*
148 	 * decide which types of pages we want to reactivate instead of freeing
149 	 * to keep usage within the minimum and maximum usage limits.
150 	 */
151 
152 	t = s->s_active + s->s_inactive + uvmexp.free;
153 	anonunder = uvmexp.anonpages <= UVM_PCTPARAM_APPLY(&s->s_anonmin, t);
154 	fileunder = uvmexp.filepages <= UVM_PCTPARAM_APPLY(&s->s_filemin, t);
155 	execunder = uvmexp.execpages <= UVM_PCTPARAM_APPLY(&s->s_execmin, t);
156 	anonover = uvmexp.anonpages > UVM_PCTPARAM_APPLY(&s->s_anonmax, t);
157 	fileover = uvmexp.filepages > UVM_PCTPARAM_APPLY(&s->s_filemax, t);
158 	execover = uvmexp.execpages > UVM_PCTPARAM_APPLY(&s->s_execmax, t);
159 	anonreact = anonunder || (!anonover && (fileover || execover));
160 	filereact = fileunder || (!fileover && (anonover || execover));
161 	execreact = execunder || (!execover && (anonover || fileover));
162 	if (filereact && execreact && (anonreact || uvm_swapisfull())) {
163 		anonreact = filereact = execreact = false;
164 	}
165 	ss->ss_anonreact = anonreact;
166 	ss->ss_filereact = filereact;
167 	ss->ss_execreact = execreact;
168 
169 	ss->ss_first = true;
170 }
171 
172 struct vm_page *
173 uvmpdpol_selectvictim(void)
174 {
175 	struct uvmpdpol_scanstate *ss = &pdpol_scanstate;
176 	struct vm_page *pg;
177 
178 	KASSERT(mutex_owned(&uvm_pageqlock));
179 
180 	while (/* CONSTCOND */ 1) {
181 		struct vm_anon *anon;
182 		struct uvm_object *uobj;
183 
184 		if (ss->ss_first) {
185 			pg = TAILQ_FIRST(&pdpol_state.s_inactiveq);
186 			ss->ss_first = false;
187 		} else {
188 			pg = ss->ss_nextpg;
189 			if (pg != NULL && (pg->pqflags & PQ_INACTIVE) == 0) {
190 				pg = TAILQ_FIRST(&pdpol_state.s_inactiveq);
191 			}
192 		}
193 		if (pg == NULL) {
194 			break;
195 		}
196 		ss->ss_nextpg = TAILQ_NEXT(pg, pageq.queue);
197 
198 		uvmexp.pdscans++;
199 
200 		/*
201 		 * move referenced pages back to active queue and
202 		 * skip to next page.
203 		 */
204 
205 		if (pmap_is_referenced(pg)) {
206 			uvmpdpol_pageactivate(pg);
207 			uvmexp.pdreact++;
208 			continue;
209 		}
210 
211 		anon = pg->uanon;
212 		uobj = pg->uobject;
213 
214 		/*
215 		 * enforce the minimum thresholds on different
216 		 * types of memory usage.  if reusing the current
217 		 * page would reduce that type of usage below its
218 		 * minimum, reactivate the page instead and move
219 		 * on to the next page.
220 		 */
221 
222 		if (uobj && UVM_OBJ_IS_VTEXT(uobj) && ss->ss_execreact) {
223 			uvmpdpol_pageactivate(pg);
224 			PDPOL_EVCNT_INCR(reactexec);
225 			continue;
226 		}
227 		if (uobj && UVM_OBJ_IS_VNODE(uobj) &&
228 		    !UVM_OBJ_IS_VTEXT(uobj) && ss->ss_filereact) {
229 			uvmpdpol_pageactivate(pg);
230 			PDPOL_EVCNT_INCR(reactfile);
231 			continue;
232 		}
233 		if ((anon || UVM_OBJ_IS_AOBJ(uobj)) && ss->ss_anonreact) {
234 			uvmpdpol_pageactivate(pg);
235 			PDPOL_EVCNT_INCR(reactanon);
236 			continue;
237 		}
238 
239 		break;
240 	}
241 
242 	return pg;
243 }
244 
245 void
246 uvmpdpol_balancequeue(int swap_shortage)
247 {
248 	int inactive_shortage;
249 	struct vm_page *p, *nextpg;
250 
251 	/*
252 	 * we have done the scan to get free pages.   now we work on meeting
253 	 * our inactive target.
254 	 */
255 
256 	inactive_shortage = pdpol_state.s_inactarg - pdpol_state.s_inactive;
257 	for (p = TAILQ_FIRST(&pdpol_state.s_activeq);
258 	     p != NULL && (inactive_shortage > 0 || swap_shortage > 0);
259 	     p = nextpg) {
260 		nextpg = TAILQ_NEXT(p, pageq.queue);
261 
262 		/*
263 		 * if there's a shortage of swap slots, try to free it.
264 		 */
265 
266 		if (swap_shortage > 0 && (p->pqflags & PQ_SWAPBACKED) != 0) {
267 			if (uvmpd_trydropswap(p)) {
268 				swap_shortage--;
269 			}
270 		}
271 
272 		/*
273 		 * if there's a shortage of inactive pages, deactivate.
274 		 */
275 
276 		if (inactive_shortage > 0) {
277 			/* no need to check wire_count as pg is "active" */
278 			uvmpdpol_pagedeactivate(p);
279 			uvmexp.pddeact++;
280 			inactive_shortage--;
281 		}
282 	}
283 }
284 
285 void
286 uvmpdpol_pagedeactivate(struct vm_page *pg)
287 {
288 
289 	KASSERT(mutex_owned(&uvm_pageqlock));
290 	if (pg->pqflags & PQ_ACTIVE) {
291 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue);
292 		pg->pqflags &= ~PQ_ACTIVE;
293 		KASSERT(pdpol_state.s_active > 0);
294 		pdpol_state.s_active--;
295 	}
296 	if ((pg->pqflags & PQ_INACTIVE) == 0) {
297 		KASSERT(pg->wire_count == 0);
298 		pmap_clear_reference(pg);
299 		TAILQ_INSERT_TAIL(&pdpol_state.s_inactiveq, pg, pageq.queue);
300 		pg->pqflags |= PQ_INACTIVE;
301 		pdpol_state.s_inactive++;
302 	}
303 }
304 
305 void
306 uvmpdpol_pageactivate(struct vm_page *pg)
307 {
308 
309 	uvmpdpol_pagedequeue(pg);
310 	TAILQ_INSERT_TAIL(&pdpol_state.s_activeq, pg, pageq.queue);
311 	pg->pqflags |= PQ_ACTIVE;
312 	pdpol_state.s_active++;
313 }
314 
315 void
316 uvmpdpol_pagedequeue(struct vm_page *pg)
317 {
318 
319 	if (pg->pqflags & PQ_ACTIVE) {
320 		KASSERT(mutex_owned(&uvm_pageqlock));
321 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue);
322 		pg->pqflags &= ~PQ_ACTIVE;
323 		KASSERT(pdpol_state.s_active > 0);
324 		pdpol_state.s_active--;
325 	} else if (pg->pqflags & PQ_INACTIVE) {
326 		KASSERT(mutex_owned(&uvm_pageqlock));
327 		TAILQ_REMOVE(&pdpol_state.s_inactiveq, pg, pageq.queue);
328 		pg->pqflags &= ~PQ_INACTIVE;
329 		KASSERT(pdpol_state.s_inactive > 0);
330 		pdpol_state.s_inactive--;
331 	}
332 }
333 
334 void
335 uvmpdpol_pageenqueue(struct vm_page *pg)
336 {
337 
338 	uvmpdpol_pageactivate(pg);
339 }
340 
341 void
342 uvmpdpol_anfree(struct vm_anon *an)
343 {
344 }
345 
346 bool
347 uvmpdpol_pageisqueued_p(struct vm_page *pg)
348 {
349 
350 	return (pg->pqflags & (PQ_ACTIVE | PQ_INACTIVE)) != 0;
351 }
352 
353 void
354 uvmpdpol_estimatepageable(int *active, int *inactive)
355 {
356 
357 	if (active) {
358 		*active = pdpol_state.s_active;
359 	}
360 	if (inactive) {
361 		*inactive = pdpol_state.s_inactive;
362 	}
363 }
364 
365 #if !defined(PDSIM)
366 static int
367 min_check(struct uvm_pctparam *pct, int t)
368 {
369 	struct uvmpdpol_globalstate *s = &pdpol_state;
370 	int total = t;
371 
372 	if (pct != &s->s_anonmin) {
373 		total += uvm_pctparam_get(&s->s_anonmin);
374 	}
375 	if (pct != &s->s_filemin) {
376 		total += uvm_pctparam_get(&s->s_filemin);
377 	}
378 	if (pct != &s->s_execmin) {
379 		total += uvm_pctparam_get(&s->s_execmin);
380 	}
381 	if (total > 95) {
382 		return EINVAL;
383 	}
384 	return 0;
385 }
386 #endif /* !defined(PDSIM) */
387 
388 void
389 uvmpdpol_init(void)
390 {
391 	struct uvmpdpol_globalstate *s = &pdpol_state;
392 
393 	TAILQ_INIT(&s->s_activeq);
394 	TAILQ_INIT(&s->s_inactiveq);
395 	uvm_pctparam_init(&s->s_inactivepct, CLOCK_INACTIVEPCT, NULL);
396 	uvm_pctparam_init(&s->s_anonmin, 10, min_check);
397 	uvm_pctparam_init(&s->s_filemin, 10, min_check);
398 	uvm_pctparam_init(&s->s_execmin,  5, min_check);
399 	uvm_pctparam_init(&s->s_anonmax, 80, NULL);
400 	uvm_pctparam_init(&s->s_filemax, 50, NULL);
401 	uvm_pctparam_init(&s->s_execmax, 30, NULL);
402 }
403 
404 void
405 uvmpdpol_reinit(void)
406 {
407 }
408 
409 bool
410 uvmpdpol_needsscan_p(void)
411 {
412 
413 	return pdpol_state.s_inactive < pdpol_state.s_inactarg;
414 }
415 
416 void
417 uvmpdpol_tune(void)
418 {
419 
420 	clock_tune();
421 }
422 
423 #if !defined(PDSIM)
424 
425 #include <sys/sysctl.h>	/* XXX SYSCTL_DESCR */
426 
427 void
428 uvmpdpol_sysctlsetup(void)
429 {
430 	struct uvmpdpol_globalstate *s = &pdpol_state;
431 
432 	uvm_pctparam_createsysctlnode(&s->s_anonmin, "anonmin",
433 	    SYSCTL_DESCR("Percentage of physical memory reserved "
434 	    "for anonymous application data"));
435 	uvm_pctparam_createsysctlnode(&s->s_filemin, "filemin",
436 	    SYSCTL_DESCR("Percentage of physical memory reserved "
437 	    "for cached file data"));
438 	uvm_pctparam_createsysctlnode(&s->s_execmin, "execmin",
439 	    SYSCTL_DESCR("Percentage of physical memory reserved "
440 	    "for cached executable data"));
441 
442 	uvm_pctparam_createsysctlnode(&s->s_anonmax, "anonmax",
443 	    SYSCTL_DESCR("Percentage of physical memory which will "
444 	    "be reclaimed from other usage for "
445 	    "anonymous application data"));
446 	uvm_pctparam_createsysctlnode(&s->s_filemax, "filemax",
447 	    SYSCTL_DESCR("Percentage of physical memory which will "
448 	    "be reclaimed from other usage for cached "
449 	    "file data"));
450 	uvm_pctparam_createsysctlnode(&s->s_execmax, "execmax",
451 	    SYSCTL_DESCR("Percentage of physical memory which will "
452 	    "be reclaimed from other usage for cached "
453 	    "executable data"));
454 
455 	uvm_pctparam_createsysctlnode(&s->s_inactivepct, "inactivepct",
456 	    SYSCTL_DESCR("Percentage of inactive queue of "
457 	    "the entire (active + inactive) queue"));
458 }
459 
460 #endif /* !defined(PDSIM) */
461 
462 #if defined(PDSIM)
463 void
464 pdsim_dump(const char *id)
465 {
466 #if defined(DEBUG)
467 	/* XXX */
468 #endif /* defined(DEBUG) */
469 }
470 #endif /* defined(PDSIM) */
471