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 | |