Home | History | Annotate | Line # | Download | only in uvm
uvm_pdpolicy_clock.c revision 1.14
      1 /*	$NetBSD: uvm_pdpolicy_clock.c,v 1.14 2011/06/12 03:36:04 rmind 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.14 2011/06/12 03:36:04 rmind 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