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