1 /* $NetBSD: uvm_pdpolicy_clock.c,v 1.12 2008/06/04 12:41:40 ad 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.12 2008/06/04 12:41:40 ad Exp $"); 78 79 #include <sys/param.h> 80 #include <sys/proc.h> 81 #include <sys/systm.h> 82 #include <sys/kernel.h> 83 84 #include <uvm/uvm.h> 85 #include <uvm/uvm_pdpolicy.h> 86 #include <uvm/uvm_pdpolicy_impl.h> 87 88 #endif /* defined(PDSIM) */ 89 90 #define PQ_INACTIVE PQ_PRIVATE1 /* page is in inactive list */ 91 #define PQ_ACTIVE PQ_PRIVATE2 /* page is in active list */ 92 93 #if !defined(CLOCK_INACTIVEPCT) 94 #define CLOCK_INACTIVEPCT 33 95 #endif /* !defined(CLOCK_INACTIVEPCT) */ 96 97 struct uvmpdpol_globalstate { 98 struct pglist s_activeq; /* allocated pages, in use */ 99 struct pglist s_inactiveq; /* pages between the clock hands */ 100 int s_active; 101 int s_inactive; 102 int s_inactarg; 103 struct uvm_pctparam s_anonmin; 104 struct uvm_pctparam s_filemin; 105 struct uvm_pctparam s_execmin; 106 struct uvm_pctparam s_anonmax; 107 struct uvm_pctparam s_filemax; 108 struct uvm_pctparam s_execmax; 109 struct uvm_pctparam s_inactivepct; 110 }; 111 112 struct uvmpdpol_scanstate { 113 bool ss_first; 114 bool ss_anonreact, ss_filereact, ss_execreact; 115 struct vm_page *ss_nextpg; 116 }; 117 118 static struct uvmpdpol_globalstate pdpol_state; 119 static struct uvmpdpol_scanstate pdpol_scanstate; 120 121 PDPOL_EVCNT_DEFINE(reactexec) 122 PDPOL_EVCNT_DEFINE(reactfile) 123 PDPOL_EVCNT_DEFINE(reactanon) 124 125 static void 126 clock_tune(void) 127 { 128 struct uvmpdpol_globalstate *s = &pdpol_state; 129 130 s->s_inactarg = UVM_PCTPARAM_APPLY(&s->s_inactivepct, 131 s->s_active + s->s_inactive); 132 if (s->s_inactarg <= uvmexp.freetarg) { 133 s->s_inactarg = uvmexp.freetarg + 1; 134 } 135 } 136 137 void 138 uvmpdpol_scaninit(void) 139 { 140 struct uvmpdpol_globalstate *s = &pdpol_state; 141 struct uvmpdpol_scanstate *ss = &pdpol_scanstate; 142 int t; 143 bool anonunder, fileunder, execunder; 144 bool anonover, fileover, execover; 145 bool anonreact, filereact, execreact; 146 147 /* 148 * decide which types of pages we want to reactivate instead of freeing 149 * to keep usage within the minimum and maximum usage limits. 150 */ 151 152 t = s->s_active + s->s_inactive + uvmexp.free; 153 anonunder = uvmexp.anonpages <= UVM_PCTPARAM_APPLY(&s->s_anonmin, t); 154 fileunder = uvmexp.filepages <= UVM_PCTPARAM_APPLY(&s->s_filemin, t); 155 execunder = uvmexp.execpages <= UVM_PCTPARAM_APPLY(&s->s_execmin, t); 156 anonover = uvmexp.anonpages > UVM_PCTPARAM_APPLY(&s->s_anonmax, t); 157 fileover = uvmexp.filepages > UVM_PCTPARAM_APPLY(&s->s_filemax, t); 158 execover = uvmexp.execpages > UVM_PCTPARAM_APPLY(&s->s_execmax, t); 159 anonreact = anonunder || (!anonover && (fileover || execover)); 160 filereact = fileunder || (!fileover && (anonover || execover)); 161 execreact = execunder || (!execover && (anonover || fileover)); 162 if (filereact && execreact && (anonreact || uvm_swapisfull())) { 163 anonreact = filereact = execreact = false; 164 } 165 ss->ss_anonreact = anonreact; 166 ss->ss_filereact = filereact; 167 ss->ss_execreact = execreact; 168 169 ss->ss_first = true; 170 } 171 172 struct vm_page * 173 uvmpdpol_selectvictim(void) 174 { 175 struct uvmpdpol_scanstate *ss = &pdpol_scanstate; 176 struct vm_page *pg; 177 178 KASSERT(mutex_owned(&uvm_pageqlock)); 179 180 while (/* CONSTCOND */ 1) { 181 struct vm_anon *anon; 182 struct uvm_object *uobj; 183 184 if (ss->ss_first) { 185 pg = TAILQ_FIRST(&pdpol_state.s_inactiveq); 186 ss->ss_first = false; 187 } else { 188 pg = ss->ss_nextpg; 189 if (pg != NULL && (pg->pqflags & PQ_INACTIVE) == 0) { 190 pg = TAILQ_FIRST(&pdpol_state.s_inactiveq); 191 } 192 } 193 if (pg == NULL) { 194 break; 195 } 196 ss->ss_nextpg = TAILQ_NEXT(pg, pageq.queue); 197 198 uvmexp.pdscans++; 199 200 /* 201 * move referenced pages back to active queue and 202 * skip to next page. 203 */ 204 205 if (pmap_is_referenced(pg)) { 206 uvmpdpol_pageactivate(pg); 207 uvmexp.pdreact++; 208 continue; 209 } 210 211 anon = pg->uanon; 212 uobj = pg->uobject; 213 214 /* 215 * enforce the minimum thresholds on different 216 * types of memory usage. if reusing the current 217 * page would reduce that type of usage below its 218 * minimum, reactivate the page instead and move 219 * on to the next page. 220 */ 221 222 if (uobj && UVM_OBJ_IS_VTEXT(uobj) && ss->ss_execreact) { 223 uvmpdpol_pageactivate(pg); 224 PDPOL_EVCNT_INCR(reactexec); 225 continue; 226 } 227 if (uobj && UVM_OBJ_IS_VNODE(uobj) && 228 !UVM_OBJ_IS_VTEXT(uobj) && ss->ss_filereact) { 229 uvmpdpol_pageactivate(pg); 230 PDPOL_EVCNT_INCR(reactfile); 231 continue; 232 } 233 if ((anon || UVM_OBJ_IS_AOBJ(uobj)) && ss->ss_anonreact) { 234 uvmpdpol_pageactivate(pg); 235 PDPOL_EVCNT_INCR(reactanon); 236 continue; 237 } 238 239 break; 240 } 241 242 return pg; 243 } 244 245 void 246 uvmpdpol_balancequeue(int swap_shortage) 247 { 248 int inactive_shortage; 249 struct vm_page *p, *nextpg; 250 251 /* 252 * we have done the scan to get free pages. now we work on meeting 253 * our inactive target. 254 */ 255 256 inactive_shortage = pdpol_state.s_inactarg - pdpol_state.s_inactive; 257 for (p = TAILQ_FIRST(&pdpol_state.s_activeq); 258 p != NULL && (inactive_shortage > 0 || swap_shortage > 0); 259 p = nextpg) { 260 nextpg = TAILQ_NEXT(p, pageq.queue); 261 262 /* 263 * if there's a shortage of swap slots, try to free it. 264 */ 265 266 if (swap_shortage > 0 && (p->pqflags & PQ_SWAPBACKED) != 0) { 267 if (uvmpd_trydropswap(p)) { 268 swap_shortage--; 269 } 270 } 271 272 /* 273 * if there's a shortage of inactive pages, deactivate. 274 */ 275 276 if (inactive_shortage > 0) { 277 /* no need to check wire_count as pg is "active" */ 278 uvmpdpol_pagedeactivate(p); 279 uvmexp.pddeact++; 280 inactive_shortage--; 281 } 282 } 283 } 284 285 void 286 uvmpdpol_pagedeactivate(struct vm_page *pg) 287 { 288 289 KASSERT(mutex_owned(&uvm_pageqlock)); 290 if (pg->pqflags & PQ_ACTIVE) { 291 TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue); 292 pg->pqflags &= ~PQ_ACTIVE; 293 KASSERT(pdpol_state.s_active > 0); 294 pdpol_state.s_active--; 295 } 296 if ((pg->pqflags & PQ_INACTIVE) == 0) { 297 KASSERT(pg->wire_count == 0); 298 pmap_clear_reference(pg); 299 TAILQ_INSERT_TAIL(&pdpol_state.s_inactiveq, pg, pageq.queue); 300 pg->pqflags |= PQ_INACTIVE; 301 pdpol_state.s_inactive++; 302 } 303 } 304 305 void 306 uvmpdpol_pageactivate(struct vm_page *pg) 307 { 308 309 uvmpdpol_pagedequeue(pg); 310 TAILQ_INSERT_TAIL(&pdpol_state.s_activeq, pg, pageq.queue); 311 pg->pqflags |= PQ_ACTIVE; 312 pdpol_state.s_active++; 313 } 314 315 void 316 uvmpdpol_pagedequeue(struct vm_page *pg) 317 { 318 319 if (pg->pqflags & PQ_ACTIVE) { 320 KASSERT(mutex_owned(&uvm_pageqlock)); 321 TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue); 322 pg->pqflags &= ~PQ_ACTIVE; 323 KASSERT(pdpol_state.s_active > 0); 324 pdpol_state.s_active--; 325 } else if (pg->pqflags & PQ_INACTIVE) { 326 KASSERT(mutex_owned(&uvm_pageqlock)); 327 TAILQ_REMOVE(&pdpol_state.s_inactiveq, pg, pageq.queue); 328 pg->pqflags &= ~PQ_INACTIVE; 329 KASSERT(pdpol_state.s_inactive > 0); 330 pdpol_state.s_inactive--; 331 } 332 } 333 334 void 335 uvmpdpol_pageenqueue(struct vm_page *pg) 336 { 337 338 uvmpdpol_pageactivate(pg); 339 } 340 341 void 342 uvmpdpol_anfree(struct vm_anon *an) 343 { 344 } 345 346 bool 347 uvmpdpol_pageisqueued_p(struct vm_page *pg) 348 { 349 350 return (pg->pqflags & (PQ_ACTIVE | PQ_INACTIVE)) != 0; 351 } 352 353 void 354 uvmpdpol_estimatepageable(int *active, int *inactive) 355 { 356 357 if (active) { 358 *active = pdpol_state.s_active; 359 } 360 if (inactive) { 361 *inactive = pdpol_state.s_inactive; 362 } 363 } 364 365 #if !defined(PDSIM) 366 static int 367 min_check(struct uvm_pctparam *pct, int t) 368 { 369 struct uvmpdpol_globalstate *s = &pdpol_state; 370 int total = t; 371 372 if (pct != &s->s_anonmin) { 373 total += uvm_pctparam_get(&s->s_anonmin); 374 } 375 if (pct != &s->s_filemin) { 376 total += uvm_pctparam_get(&s->s_filemin); 377 } 378 if (pct != &s->s_execmin) { 379 total += uvm_pctparam_get(&s->s_execmin); 380 } 381 if (total > 95) { 382 return EINVAL; 383 } 384 return 0; 385 } 386 #endif /* !defined(PDSIM) */ 387 388 void 389 uvmpdpol_init(void) 390 { 391 struct uvmpdpol_globalstate *s = &pdpol_state; 392 393 TAILQ_INIT(&s->s_activeq); 394 TAILQ_INIT(&s->s_inactiveq); 395 uvm_pctparam_init(&s->s_inactivepct, CLOCK_INACTIVEPCT, NULL); 396 uvm_pctparam_init(&s->s_anonmin, 10, min_check); 397 uvm_pctparam_init(&s->s_filemin, 10, min_check); 398 uvm_pctparam_init(&s->s_execmin, 5, min_check); 399 uvm_pctparam_init(&s->s_anonmax, 80, NULL); 400 uvm_pctparam_init(&s->s_filemax, 50, NULL); 401 uvm_pctparam_init(&s->s_execmax, 30, NULL); 402 } 403 404 void 405 uvmpdpol_reinit(void) 406 { 407 } 408 409 bool 410 uvmpdpol_needsscan_p(void) 411 { 412 413 return pdpol_state.s_inactive < pdpol_state.s_inactarg; 414 } 415 416 void 417 uvmpdpol_tune(void) 418 { 419 420 clock_tune(); 421 } 422 423 #if !defined(PDSIM) 424 425 #include <sys/sysctl.h> /* XXX SYSCTL_DESCR */ 426 427 void 428 uvmpdpol_sysctlsetup(void) 429 { 430 struct uvmpdpol_globalstate *s = &pdpol_state; 431 432 uvm_pctparam_createsysctlnode(&s->s_anonmin, "anonmin", 433 SYSCTL_DESCR("Percentage of physical memory reserved " 434 "for anonymous application data")); 435 uvm_pctparam_createsysctlnode(&s->s_filemin, "filemin", 436 SYSCTL_DESCR("Percentage of physical memory reserved " 437 "for cached file data")); 438 uvm_pctparam_createsysctlnode(&s->s_execmin, "execmin", 439 SYSCTL_DESCR("Percentage of physical memory reserved " 440 "for cached executable data")); 441 442 uvm_pctparam_createsysctlnode(&s->s_anonmax, "anonmax", 443 SYSCTL_DESCR("Percentage of physical memory which will " 444 "be reclaimed from other usage for " 445 "anonymous application data")); 446 uvm_pctparam_createsysctlnode(&s->s_filemax, "filemax", 447 SYSCTL_DESCR("Percentage of physical memory which will " 448 "be reclaimed from other usage for cached " 449 "file data")); 450 uvm_pctparam_createsysctlnode(&s->s_execmax, "execmax", 451 SYSCTL_DESCR("Percentage of physical memory which will " 452 "be reclaimed from other usage for cached " 453 "executable data")); 454 455 uvm_pctparam_createsysctlnode(&s->s_inactivepct, "inactivepct", 456 SYSCTL_DESCR("Percentage of inactive queue of " 457 "the entire (active + inactive) queue")); 458 } 459 460 #endif /* !defined(PDSIM) */ 461 462 #if defined(PDSIM) 463 void 464 pdsim_dump(const char *id) 465 { 466 #if defined(DEBUG) 467 /* XXX */ 468 #endif /* defined(DEBUG) */ 469 } 470 #endif /* defined(PDSIM) */ 471