xref: /netbsd-src/sys/uvm/uvm_pdpolicy_clock.c (revision 1ad9454efb13a65cd7535ccf867508cb14d9d30e)
1 /*	$NetBSD: uvm_pdpolicy_clock.c,v 1.2 2006/09/15 15:51:13 yamt 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.2 2006/09/15 15:51:13 yamt Exp $");
78 
79 #include <sys/param.h>
80 #include <sys/proc.h>
81 #include <sys/systm.h>
82 #include <sys/kernel.h>
83 #include <sys/vnode.h>
84 
85 #include <uvm/uvm.h>
86 #include <uvm/uvm_pdpolicy.h>
87 #include <uvm/uvm_pdpolicy_impl.h>
88 
89 #endif /* defined(PDSIM) */
90 
91 #define PQ_INACTIVE	PQ_PRIVATE1	/* page is in inactive list */
92 #define PQ_ACTIVE	PQ_PRIVATE2	/* page is in active list */
93 
94 #if !defined(CLOCK_INACTIVEPCT)
95 #define	CLOCK_INACTIVEPCT	33
96 #endif /* !defined(CLOCK_INACTIVEPCT) */
97 
98 struct uvmpdpol_globalstate {
99 	struct pglist s_activeq;	/* allocated pages, in use */
100 	struct pglist s_inactiveq;	/* pages between the clock hands */
101 	int s_active;
102 	int s_inactive;
103 	int s_inactarg;
104 	struct uvm_pctparam s_anonmin;
105 	struct uvm_pctparam s_filemin;
106 	struct uvm_pctparam s_execmin;
107 	struct uvm_pctparam s_anonmax;
108 	struct uvm_pctparam s_filemax;
109 	struct uvm_pctparam s_execmax;
110 	struct uvm_pctparam s_inactivepct;
111 };
112 
113 struct uvmpdpol_scanstate {
114 	boolean_t ss_first;
115 	boolean_t ss_anonreact, ss_filereact, ss_execreact;
116 	struct vm_page *ss_nextpg;
117 };
118 
119 static struct uvmpdpol_globalstate pdpol_state;
120 static struct uvmpdpol_scanstate pdpol_scanstate;
121 
122 PDPOL_EVCNT_DEFINE(reactexec)
123 PDPOL_EVCNT_DEFINE(reactfile)
124 PDPOL_EVCNT_DEFINE(reactanon)
125 
126 static void
127 clock_tune(void)
128 {
129 	struct uvmpdpol_globalstate *s = &pdpol_state;
130 
131 	s->s_inactarg =
132 	s->s_inactarg = UVM_PCTPARAM_APPLY(&s->s_inactivepct,
133 	    s->s_active + s->s_inactive);
134 	if (s->s_inactarg <= uvmexp.freetarg) {
135 		s->s_inactarg = uvmexp.freetarg + 1;
136 	}
137 }
138 
139 void
140 uvmpdpol_scaninit(void)
141 {
142 	struct uvmpdpol_globalstate *s = &pdpol_state;
143 	struct uvmpdpol_scanstate *ss = &pdpol_scanstate;
144 	int t;
145 	boolean_t anonunder, fileunder, execunder;
146 	boolean_t anonover, fileover, execover;
147 	boolean_t anonreact, filereact, execreact;
148 
149 	/*
150 	 * decide which types of pages we want to reactivate instead of freeing
151 	 * to keep usage within the minimum and maximum usage limits.
152 	 */
153 
154 	t = s->s_active + s->s_inactive + uvmexp.free;
155 	anonunder = uvmexp.anonpages <= UVM_PCTPARAM_APPLY(&s->s_anonmin, t);
156 	fileunder = uvmexp.filepages <= UVM_PCTPARAM_APPLY(&s->s_filemin, t);
157 	execunder = uvmexp.execpages <= UVM_PCTPARAM_APPLY(&s->s_execmin, t);
158 	anonover = uvmexp.anonpages > UVM_PCTPARAM_APPLY(&s->s_anonmax, t);
159 	fileover = uvmexp.filepages > UVM_PCTPARAM_APPLY(&s->s_filemax, t);
160 	execover = uvmexp.execpages > UVM_PCTPARAM_APPLY(&s->s_execmax, t);
161 	anonreact = anonunder || (!anonover && (fileover || execover));
162 	filereact = fileunder || (!fileover && (anonover || execover));
163 	execreact = execunder || (!execover && (anonover || fileover));
164 	if (filereact && execreact && (anonreact || uvm_swapisfull())) {
165 		anonreact = filereact = execreact = FALSE;
166 	}
167 	ss->ss_anonreact = anonreact;
168 	ss->ss_filereact = filereact;
169 	ss->ss_execreact = execreact;
170 
171 	ss->ss_first = TRUE;
172 }
173 
174 struct vm_page *
175 uvmpdpol_selectvictim(void)
176 {
177 	struct uvmpdpol_scanstate *ss = &pdpol_scanstate;
178 	struct vm_page *pg;
179 
180 	UVM_LOCK_ASSERT_PAGEQ();
181 
182 	while (/* CONSTCOND */ 1) {
183 		struct vm_anon *anon;
184 		struct uvm_object *uobj;
185 
186 		if (ss->ss_first) {
187 			pg = TAILQ_FIRST(&pdpol_state.s_inactiveq);
188 			ss->ss_first = FALSE;
189 		} else {
190 			pg = ss->ss_nextpg;
191 			if (pg != NULL && (pg->pqflags & PQ_INACTIVE) == 0) {
192 				pg = TAILQ_FIRST(&pdpol_state.s_inactiveq);
193 			}
194 		}
195 		if (pg == NULL) {
196 			break;
197 		}
198 		ss->ss_nextpg = TAILQ_NEXT(pg, pageq);
199 
200 		uvmexp.pdscans++;
201 
202 		/*
203 		 * move referenced pages back to active queue and
204 		 * skip to next page.
205 		 */
206 
207 		if (pmap_is_referenced(pg)) {
208 			uvmpdpol_pageactivate(pg);
209 			uvmexp.pdreact++;
210 			continue;
211 		}
212 
213 		anon = pg->uanon;
214 		uobj = pg->uobject;
215 
216 		/*
217 		 * enforce the minimum thresholds on different
218 		 * types of memory usage.  if reusing the current
219 		 * page would reduce that type of usage below its
220 		 * minimum, reactivate the page instead and move
221 		 * on to the next page.
222 		 */
223 
224 		if (uobj && UVM_OBJ_IS_VTEXT(uobj) && ss->ss_execreact) {
225 			uvmpdpol_pageactivate(pg);
226 			PDPOL_EVCNT_INCR(reactexec);
227 			continue;
228 		}
229 		if (uobj && UVM_OBJ_IS_VNODE(uobj) &&
230 		    !UVM_OBJ_IS_VTEXT(uobj) && ss->ss_filereact) {
231 			uvmpdpol_pageactivate(pg);
232 			PDPOL_EVCNT_INCR(reactfile);
233 			continue;
234 		}
235 		if ((anon || UVM_OBJ_IS_AOBJ(uobj)) && ss->ss_anonreact) {
236 			uvmpdpol_pageactivate(pg);
237 			PDPOL_EVCNT_INCR(reactanon);
238 			continue;
239 		}
240 
241 		break;
242 	}
243 
244 	return pg;
245 }
246 
247 void
248 uvmpdpol_balancequeue(int swap_shortage)
249 {
250 	int inactive_shortage;
251 	struct vm_page *p, *nextpg;
252 
253 	/*
254 	 * we have done the scan to get free pages.   now we work on meeting
255 	 * our inactive target.
256 	 */
257 
258 	inactive_shortage = pdpol_state.s_inactarg - pdpol_state.s_inactive;
259 	for (p = TAILQ_FIRST(&pdpol_state.s_activeq);
260 	     p != NULL && (inactive_shortage > 0 || swap_shortage > 0);
261 	     p = nextpg) {
262 		nextpg = TAILQ_NEXT(p, pageq);
263 
264 		/*
265 		 * if there's a shortage of swap slots, try to free it.
266 		 */
267 
268 		if (swap_shortage > 0 && (p->pqflags & PQ_SWAPBACKED) != 0) {
269 			if (uvmpd_trydropswap(p)) {
270 				swap_shortage--;
271 			}
272 		}
273 
274 		/*
275 		 * if there's a shortage of inactive pages, deactivate.
276 		 */
277 
278 		if (inactive_shortage > 0) {
279 			/* no need to check wire_count as pg is "active" */
280 			pmap_clear_reference(p);
281 			uvmpdpol_pagedeactivate(p);
282 			uvmexp.pddeact++;
283 			inactive_shortage--;
284 		}
285 	}
286 }
287 
288 void
289 uvmpdpol_pagedeactivate(struct vm_page *pg)
290 {
291 
292 	UVM_LOCK_ASSERT_PAGEQ();
293 	if (pg->pqflags & PQ_ACTIVE) {
294 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq);
295 		pg->pqflags &= ~PQ_ACTIVE;
296 		KASSERT(pdpol_state.s_active > 0);
297 		pdpol_state.s_active--;
298 	}
299 	if ((pg->pqflags & PQ_INACTIVE) == 0) {
300 		KASSERT(pg->wire_count == 0);
301 		TAILQ_INSERT_TAIL(&pdpol_state.s_inactiveq, pg, pageq);
302 		pg->pqflags |= PQ_INACTIVE;
303 		pdpol_state.s_inactive++;
304 	}
305 }
306 
307 void
308 uvmpdpol_pageactivate(struct vm_page *pg)
309 {
310 
311 	uvmpdpol_pagedequeue(pg);
312 	TAILQ_INSERT_TAIL(&pdpol_state.s_activeq, pg, pageq);
313 	pg->pqflags |= PQ_ACTIVE;
314 	pdpol_state.s_active++;
315 }
316 
317 void
318 uvmpdpol_pagedequeue(struct vm_page *pg)
319 {
320 
321 	if (pg->pqflags & PQ_ACTIVE) {
322 		UVM_LOCK_ASSERT_PAGEQ();
323 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq);
324 		pg->pqflags &= ~PQ_ACTIVE;
325 		KASSERT(pdpol_state.s_active > 0);
326 		pdpol_state.s_active--;
327 	} else if (pg->pqflags & PQ_INACTIVE) {
328 		UVM_LOCK_ASSERT_PAGEQ();
329 		TAILQ_REMOVE(&pdpol_state.s_inactiveq, pg, pageq);
330 		pg->pqflags &= ~PQ_INACTIVE;
331 		KASSERT(pdpol_state.s_inactive > 0);
332 		pdpol_state.s_inactive--;
333 	}
334 }
335 
336 void
337 uvmpdpol_pageenqueue(struct vm_page *pg)
338 {
339 
340 	uvmpdpol_pageactivate(pg);
341 }
342 
343 void
344 uvmpdpol_anfree(struct vm_anon *an)
345 {
346 }
347 
348 boolean_t
349 uvmpdpol_pageisqueued_p(struct vm_page *pg)
350 {
351 
352 	return (pg->pqflags & (PQ_ACTIVE | PQ_INACTIVE)) != 0;
353 }
354 
355 void
356 uvmpdpol_estimatepageable(int *active, int *inactive)
357 {
358 
359 	if (active) {
360 		*active = pdpol_state.s_active;
361 	}
362 	if (inactive) {
363 		*inactive = pdpol_state.s_inactive;
364 	}
365 }
366 
367 #if !defined(PDSIM)
368 static int
369 min_check(struct uvm_pctparam *pct, int t)
370 {
371 	struct uvmpdpol_globalstate *s = &pdpol_state;
372 	int total = t;
373 
374 	if (pct != &s->s_anonmin) {
375 		total += uvm_pctparam_get(&s->s_anonmin);
376 	}
377 	if (pct != &s->s_filemin) {
378 		total += uvm_pctparam_get(&s->s_filemin);
379 	}
380 	if (pct != &s->s_execmin) {
381 		total += uvm_pctparam_get(&s->s_execmin);
382 	}
383 	if (total > 95) {
384 		return EINVAL;
385 	}
386 	return 0;
387 }
388 #endif /* !defined(PDSIM) */
389 
390 void
391 uvmpdpol_init(void)
392 {
393 	struct uvmpdpol_globalstate *s = &pdpol_state;
394 
395 	TAILQ_INIT(&s->s_activeq);
396 	TAILQ_INIT(&s->s_inactiveq);
397 	uvm_pctparam_init(&s->s_inactivepct, CLOCK_INACTIVEPCT, NULL);
398 	uvm_pctparam_init(&s->s_anonmin, 10, min_check);
399 	uvm_pctparam_init(&s->s_filemin, 10, min_check);
400 	uvm_pctparam_init(&s->s_execmin,  5, min_check);
401 	uvm_pctparam_init(&s->s_anonmax, 80, NULL);
402 	uvm_pctparam_init(&s->s_filemax, 50, NULL);
403 	uvm_pctparam_init(&s->s_execmax, 30, NULL);
404 }
405 
406 void
407 uvmpdpol_reinit(void)
408 {
409 }
410 
411 boolean_t
412 uvmpdpol_needsscan_p(void)
413 {
414 
415 	return pdpol_state.s_inactive < pdpol_state.s_inactarg;
416 }
417 
418 void
419 uvmpdpol_tune(void)
420 {
421 
422 	clock_tune();
423 }
424 
425 #if !defined(PDSIM)
426 
427 #include <sys/sysctl.h>	/* XXX SYSCTL_DESCR */
428 
429 void
430 uvmpdpol_sysctlsetup(void)
431 {
432 	struct uvmpdpol_globalstate *s = &pdpol_state;
433 
434 	uvm_pctparam_createsysctlnode(&s->s_anonmin, "anonmin",
435 	    SYSCTL_DESCR("Percentage of physical memory reserved "
436 	    "for anonymous application data"));
437 	uvm_pctparam_createsysctlnode(&s->s_filemin, "filemin",
438 	    SYSCTL_DESCR("Percentage of physical memory reserved "
439 	    "for cached executable data"));
440 	uvm_pctparam_createsysctlnode(&s->s_execmin, "execmin",
441 	    SYSCTL_DESCR("Percentage of physical memory reserved "
442 	    "for cached file data"));
443 
444 	uvm_pctparam_createsysctlnode(&s->s_anonmax, "anonmax",
445 	    SYSCTL_DESCR("Percentage of physical memory which will "
446 	    "be reclaimed from other usage for "
447 	    "anonymous application data"));
448 	uvm_pctparam_createsysctlnode(&s->s_filemax, "filemax",
449 	    SYSCTL_DESCR("Percentage of physical memory which will "
450 	    "be reclaimed from other usage for cached "
451 	    "file data"));
452 	uvm_pctparam_createsysctlnode(&s->s_execmax, "execmax",
453 	    SYSCTL_DESCR("Percentage of physical memory which will "
454 	    "be reclaimed from other usage for cached "
455 	    "executable data"));
456 
457 	uvm_pctparam_createsysctlnode(&s->s_inactivepct, "inactivepct",
458 	    SYSCTL_DESCR("Percentage of inactive queue of "
459 	    "the entire (active + inactive) queue"));
460 }
461 
462 #endif /* !defined(PDSIM) */
463 
464 #if defined(PDSIM)
465 void
466 pdsim_dump(const char *id)
467 {
468 #if defined(DEBUG)
469 	/* XXX */
470 #endif /* defined(DEBUG) */
471 }
472 #endif /* defined(PDSIM) */
473