uvm_pdpolicy_clock.c revision 1.13 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 (at) 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