uvm_pdpolicy_clock.c revision 1.1.2.2 1 /* $NetBSD: uvm_pdpolicy_clock.c,v 1.1.2.2 2006/09/15 11:54:56 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 (at) 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.1.2.2 2006/09/15 11:54:56 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