xref: /netbsd-src/sys/uvm/uvm_pdpolicy_clock.c (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
1 /*	$NetBSD: uvm_pdpolicy_clock.c,v 1.17 2012/01/30 17:21:52 para 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.17 2012/01/30 17:21:52 para 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 	kmutex_t *lock;
173 
174 	KASSERT(mutex_owned(&uvm_pageqlock));
175 
176 	while (/* CONSTCOND */ 1) {
177 		struct vm_anon *anon;
178 		struct uvm_object *uobj;
179 
180 		if (ss->ss_first) {
181 			pg = TAILQ_FIRST(&pdpol_state.s_inactiveq);
182 			ss->ss_first = false;
183 		} else {
184 			pg = ss->ss_nextpg;
185 			if (pg != NULL && (pg->pqflags & PQ_INACTIVE) == 0) {
186 				pg = TAILQ_FIRST(&pdpol_state.s_inactiveq);
187 			}
188 		}
189 		if (pg == NULL) {
190 			break;
191 		}
192 		ss->ss_nextpg = TAILQ_NEXT(pg, pageq.queue);
193 
194 		uvmexp.pdscans++;
195 
196 		/*
197 		 * move referenced pages back to active queue and
198 		 * skip to next page.
199 		 */
200 
201 		lock = uvmpd_trylockowner(pg);
202 		if (lock != NULL) {
203 			if (pmap_is_referenced(pg)) {
204 				uvmpdpol_pageactivate(pg);
205 				uvmexp.pdreact++;
206 				mutex_exit(lock);
207 				continue;
208 			}
209 			mutex_exit(lock);
210 		}
211 
212 		anon = pg->uanon;
213 		uobj = pg->uobject;
214 
215 		/*
216 		 * enforce the minimum thresholds on different
217 		 * types of memory usage.  if reusing the current
218 		 * page would reduce that type of usage below its
219 		 * minimum, reactivate the page instead and move
220 		 * on to the next page.
221 		 */
222 
223 		if (uobj && UVM_OBJ_IS_VTEXT(uobj) && ss->ss_execreact) {
224 			uvmpdpol_pageactivate(pg);
225 			PDPOL_EVCNT_INCR(reactexec);
226 			continue;
227 		}
228 		if (uobj && UVM_OBJ_IS_VNODE(uobj) &&
229 		    !UVM_OBJ_IS_VTEXT(uobj) && ss->ss_filereact) {
230 			uvmpdpol_pageactivate(pg);
231 			PDPOL_EVCNT_INCR(reactfile);
232 			continue;
233 		}
234 		if ((anon || UVM_OBJ_IS_AOBJ(uobj)) && ss->ss_anonreact) {
235 			uvmpdpol_pageactivate(pg);
236 			PDPOL_EVCNT_INCR(reactanon);
237 			continue;
238 		}
239 
240 		break;
241 	}
242 
243 	return pg;
244 }
245 
246 void
247 uvmpdpol_balancequeue(int swap_shortage)
248 {
249 	int inactive_shortage;
250 	struct vm_page *p, *nextpg;
251 	kmutex_t *lock;
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.queue);
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 			continue;
280 		}
281 
282 		/* no need to check wire_count as pg is "active" */
283 		lock = uvmpd_trylockowner(p);
284 		if (lock != NULL) {
285 			uvmpdpol_pagedeactivate(p);
286 			uvmexp.pddeact++;
287 			inactive_shortage--;
288 			mutex_exit(lock);
289 		}
290 	}
291 }
292 
293 void
294 uvmpdpol_pagedeactivate(struct vm_page *pg)
295 {
296 
297 	KASSERT(uvm_page_locked_p(pg));
298 	KASSERT(mutex_owned(&uvm_pageqlock));
299 
300 	if (pg->pqflags & PQ_ACTIVE) {
301 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue);
302 		pg->pqflags &= ~PQ_ACTIVE;
303 		KASSERT(pdpol_state.s_active > 0);
304 		pdpol_state.s_active--;
305 	}
306 	if ((pg->pqflags & PQ_INACTIVE) == 0) {
307 		KASSERT(pg->wire_count == 0);
308 		pmap_clear_reference(pg);
309 		TAILQ_INSERT_TAIL(&pdpol_state.s_inactiveq, pg, pageq.queue);
310 		pg->pqflags |= PQ_INACTIVE;
311 		pdpol_state.s_inactive++;
312 	}
313 }
314 
315 void
316 uvmpdpol_pageactivate(struct vm_page *pg)
317 {
318 
319 	uvmpdpol_pagedequeue(pg);
320 	TAILQ_INSERT_TAIL(&pdpol_state.s_activeq, pg, pageq.queue);
321 	pg->pqflags |= PQ_ACTIVE;
322 	pdpol_state.s_active++;
323 }
324 
325 void
326 uvmpdpol_pagedequeue(struct vm_page *pg)
327 {
328 
329 	if (pg->pqflags & PQ_ACTIVE) {
330 		KASSERT(mutex_owned(&uvm_pageqlock));
331 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue);
332 		pg->pqflags &= ~PQ_ACTIVE;
333 		KASSERT(pdpol_state.s_active > 0);
334 		pdpol_state.s_active--;
335 	} else if (pg->pqflags & PQ_INACTIVE) {
336 		KASSERT(mutex_owned(&uvm_pageqlock));
337 		TAILQ_REMOVE(&pdpol_state.s_inactiveq, pg, pageq.queue);
338 		pg->pqflags &= ~PQ_INACTIVE;
339 		KASSERT(pdpol_state.s_inactive > 0);
340 		pdpol_state.s_inactive--;
341 	}
342 }
343 
344 void
345 uvmpdpol_pageenqueue(struct vm_page *pg)
346 {
347 
348 	uvmpdpol_pageactivate(pg);
349 }
350 
351 void
352 uvmpdpol_anfree(struct vm_anon *an)
353 {
354 }
355 
356 bool
357 uvmpdpol_pageisqueued_p(struct vm_page *pg)
358 {
359 
360 	return (pg->pqflags & (PQ_ACTIVE | PQ_INACTIVE)) != 0;
361 }
362 
363 void
364 uvmpdpol_estimatepageable(int *active, int *inactive)
365 {
366 
367 	if (active) {
368 		*active = pdpol_state.s_active;
369 	}
370 	if (inactive) {
371 		*inactive = pdpol_state.s_inactive;
372 	}
373 }
374 
375 #if !defined(PDSIM)
376 static int
377 min_check(struct uvm_pctparam *pct, int t)
378 {
379 	struct uvmpdpol_globalstate *s = &pdpol_state;
380 	int total = t;
381 
382 	if (pct != &s->s_anonmin) {
383 		total += uvm_pctparam_get(&s->s_anonmin);
384 	}
385 	if (pct != &s->s_filemin) {
386 		total += uvm_pctparam_get(&s->s_filemin);
387 	}
388 	if (pct != &s->s_execmin) {
389 		total += uvm_pctparam_get(&s->s_execmin);
390 	}
391 	if (total > 95) {
392 		return EINVAL;
393 	}
394 	return 0;
395 }
396 #endif /* !defined(PDSIM) */
397 
398 void
399 uvmpdpol_init(void)
400 {
401 	struct uvmpdpol_globalstate *s = &pdpol_state;
402 
403 	TAILQ_INIT(&s->s_activeq);
404 	TAILQ_INIT(&s->s_inactiveq);
405 	uvm_pctparam_init(&s->s_inactivepct, CLOCK_INACTIVEPCT, NULL);
406 	uvm_pctparam_init(&s->s_anonmin, 10, min_check);
407 	uvm_pctparam_init(&s->s_filemin, 10, min_check);
408 	uvm_pctparam_init(&s->s_execmin,  5, min_check);
409 	uvm_pctparam_init(&s->s_anonmax, 80, NULL);
410 	uvm_pctparam_init(&s->s_filemax, 50, NULL);
411 	uvm_pctparam_init(&s->s_execmax, 30, NULL);
412 }
413 
414 void
415 uvmpdpol_reinit(void)
416 {
417 }
418 
419 bool
420 uvmpdpol_needsscan_p(void)
421 {
422 
423 	return pdpol_state.s_inactive < pdpol_state.s_inactarg;
424 }
425 
426 void
427 uvmpdpol_tune(void)
428 {
429 
430 	clock_tune();
431 }
432 
433 #if !defined(PDSIM)
434 
435 #include <sys/sysctl.h>	/* XXX SYSCTL_DESCR */
436 
437 void
438 uvmpdpol_sysctlsetup(void)
439 {
440 	struct uvmpdpol_globalstate *s = &pdpol_state;
441 
442 	uvm_pctparam_createsysctlnode(&s->s_anonmin, "anonmin",
443 	    SYSCTL_DESCR("Percentage of physical memory reserved "
444 	    "for anonymous application data"));
445 	uvm_pctparam_createsysctlnode(&s->s_filemin, "filemin",
446 	    SYSCTL_DESCR("Percentage of physical memory reserved "
447 	    "for cached file data"));
448 	uvm_pctparam_createsysctlnode(&s->s_execmin, "execmin",
449 	    SYSCTL_DESCR("Percentage of physical memory reserved "
450 	    "for cached executable data"));
451 
452 	uvm_pctparam_createsysctlnode(&s->s_anonmax, "anonmax",
453 	    SYSCTL_DESCR("Percentage of physical memory which will "
454 	    "be reclaimed from other usage for "
455 	    "anonymous application data"));
456 	uvm_pctparam_createsysctlnode(&s->s_filemax, "filemax",
457 	    SYSCTL_DESCR("Percentage of physical memory which will "
458 	    "be reclaimed from other usage for cached "
459 	    "file data"));
460 	uvm_pctparam_createsysctlnode(&s->s_execmax, "execmax",
461 	    SYSCTL_DESCR("Percentage of physical memory which will "
462 	    "be reclaimed from other usage for cached "
463 	    "executable data"));
464 
465 	uvm_pctparam_createsysctlnode(&s->s_inactivepct, "inactivepct",
466 	    SYSCTL_DESCR("Percentage of inactive queue of "
467 	    "the entire (active + inactive) queue"));
468 }
469 
470 #endif /* !defined(PDSIM) */
471 
472 #if defined(PDSIM)
473 void
474 pdsim_dump(const char *id)
475 {
476 #if defined(DEBUG)
477 	/* XXX */
478 #endif /* defined(DEBUG) */
479 }
480 #endif /* defined(PDSIM) */
481