1 /* $NetBSD: uvm_pdpolicy_clock.c,v 1.2 2006/09/15 15:51:13 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@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.2 2006/09/15 15:51:13 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