1df8bae1dSRodney W. Grimes /* 2df8bae1dSRodney W. Grimes * Copyright (c) 1991, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * This code is derived from software contributed to Berkeley by 6df8bae1dSRodney W. Grimes * The Mach Operating System project at Carnegie-Mellon University. 7df8bae1dSRodney W. Grimes * 8df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 9df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 10df8bae1dSRodney W. Grimes * are met: 11df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 12df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 13df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 14df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 15df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 16df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 17df8bae1dSRodney W. Grimes * must display the following acknowledgement: 18df8bae1dSRodney W. Grimes * This product includes software developed by the University of 19df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 20df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 21df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 22df8bae1dSRodney W. Grimes * without specific prior written permission. 23df8bae1dSRodney W. Grimes * 24df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34df8bae1dSRodney W. Grimes * SUCH DAMAGE. 35df8bae1dSRodney W. Grimes * 36df8bae1dSRodney W. Grimes * @(#)vm_pageout.c 8.5 (Berkeley) 2/14/94 37df8bae1dSRodney W. Grimes * 38df8bae1dSRodney W. Grimes * 39df8bae1dSRodney W. Grimes * Copyright (c) 1987, 1990 Carnegie-Mellon University. 40df8bae1dSRodney W. Grimes * All rights reserved. 41df8bae1dSRodney W. Grimes * 42df8bae1dSRodney W. Grimes * Authors: Avadis Tevanian, Jr., Michael Wayne Young 43df8bae1dSRodney W. Grimes * 44df8bae1dSRodney W. Grimes * Permission to use, copy, modify and distribute this software and 45df8bae1dSRodney W. Grimes * its documentation is hereby granted, provided that both the copyright 46df8bae1dSRodney W. Grimes * notice and this permission notice appear in all copies of the 47df8bae1dSRodney W. Grimes * software, derivative works or modified versions, and any portions 48df8bae1dSRodney W. Grimes * thereof, and that both notices appear in supporting documentation. 49df8bae1dSRodney W. Grimes * 50df8bae1dSRodney W. Grimes * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 51df8bae1dSRodney W. Grimes * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 52df8bae1dSRodney W. Grimes * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 53df8bae1dSRodney W. Grimes * 54df8bae1dSRodney W. Grimes * Carnegie Mellon requests users of this software to return to 55df8bae1dSRodney W. Grimes * 56df8bae1dSRodney W. Grimes * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 57df8bae1dSRodney W. Grimes * School of Computer Science 58df8bae1dSRodney W. Grimes * Carnegie Mellon University 59df8bae1dSRodney W. Grimes * Pittsburgh PA 15213-3890 60df8bae1dSRodney W. Grimes * 61df8bae1dSRodney W. Grimes * any improvements or extensions that they make and grant Carnegie the 62df8bae1dSRodney W. Grimes * rights to redistribute these changes. 63df8bae1dSRodney W. Grimes */ 64df8bae1dSRodney W. Grimes 65df8bae1dSRodney W. Grimes /* 66df8bae1dSRodney W. Grimes * The proverbial page-out daemon. 67df8bae1dSRodney W. Grimes */ 68df8bae1dSRodney W. Grimes 69df8bae1dSRodney W. Grimes #include <sys/param.h> 70df8bae1dSRodney W. Grimes 71df8bae1dSRodney W. Grimes #include <vm/vm.h> 72df8bae1dSRodney W. Grimes #include <vm/vm_page.h> 73df8bae1dSRodney W. Grimes #include <vm/vm_pageout.h> 74df8bae1dSRodney W. Grimes 75df8bae1dSRodney W. Grimes #ifndef VM_PAGE_FREE_MIN 76df8bae1dSRodney W. Grimes #define VM_PAGE_FREE_MIN (cnt.v_free_count / 20) 77df8bae1dSRodney W. Grimes #endif 78df8bae1dSRodney W. Grimes 79df8bae1dSRodney W. Grimes #ifndef VM_PAGE_FREE_TARGET 80df8bae1dSRodney W. Grimes #define VM_PAGE_FREE_TARGET ((cnt.v_free_min * 4) / 3) 81df8bae1dSRodney W. Grimes #endif 82df8bae1dSRodney W. Grimes 83df8bae1dSRodney W. Grimes int vm_page_free_min_min = 16 * 1024; 84df8bae1dSRodney W. Grimes int vm_page_free_min_max = 256 * 1024; 85df8bae1dSRodney W. Grimes 86df8bae1dSRodney W. Grimes int vm_pages_needed; /* Event on which pageout daemon sleeps */ 87df8bae1dSRodney W. Grimes 88df8bae1dSRodney W. Grimes int vm_page_max_wired = 0; /* XXX max # of wired pages system-wide */ 89df8bae1dSRodney W. Grimes 90df8bae1dSRodney W. Grimes #ifdef CLUSTERED_PAGEOUT 91df8bae1dSRodney W. Grimes #define MAXPOCLUSTER (MAXPHYS/NBPG) /* XXX */ 92df8bae1dSRodney W. Grimes int doclustered_pageout = 1; 93df8bae1dSRodney W. Grimes #endif 94df8bae1dSRodney W. Grimes 95df8bae1dSRodney W. Grimes /* 96df8bae1dSRodney W. Grimes * vm_pageout_scan does the dirty work for the pageout daemon. 97df8bae1dSRodney W. Grimes */ 98df8bae1dSRodney W. Grimes void 99df8bae1dSRodney W. Grimes vm_pageout_scan() 100df8bae1dSRodney W. Grimes { 101df8bae1dSRodney W. Grimes register vm_page_t m, next; 102df8bae1dSRodney W. Grimes register int page_shortage; 103df8bae1dSRodney W. Grimes register int s; 104df8bae1dSRodney W. Grimes register int pages_freed; 105df8bae1dSRodney W. Grimes int free; 106df8bae1dSRodney W. Grimes vm_object_t object; 107df8bae1dSRodney W. Grimes 108df8bae1dSRodney W. Grimes /* 109df8bae1dSRodney W. Grimes * Only continue when we want more pages to be "free" 110df8bae1dSRodney W. Grimes */ 111df8bae1dSRodney W. Grimes 112df8bae1dSRodney W. Grimes cnt.v_rev++; 113df8bae1dSRodney W. Grimes 114df8bae1dSRodney W. Grimes s = splimp(); 115df8bae1dSRodney W. Grimes simple_lock(&vm_page_queue_free_lock); 116df8bae1dSRodney W. Grimes free = cnt.v_free_count; 117df8bae1dSRodney W. Grimes simple_unlock(&vm_page_queue_free_lock); 118df8bae1dSRodney W. Grimes splx(s); 119df8bae1dSRodney W. Grimes 120df8bae1dSRodney W. Grimes if (free < cnt.v_free_target) { 121df8bae1dSRodney W. Grimes swapout_threads(); 122df8bae1dSRodney W. Grimes 123df8bae1dSRodney W. Grimes /* 124df8bae1dSRodney W. Grimes * Be sure the pmap system is updated so 125df8bae1dSRodney W. Grimes * we can scan the inactive queue. 126df8bae1dSRodney W. Grimes */ 127df8bae1dSRodney W. Grimes 128df8bae1dSRodney W. Grimes pmap_update(); 129df8bae1dSRodney W. Grimes } 130df8bae1dSRodney W. Grimes 131df8bae1dSRodney W. Grimes /* 132df8bae1dSRodney W. Grimes * Acquire the resident page system lock, 133df8bae1dSRodney W. Grimes * as we may be changing what's resident quite a bit. 134df8bae1dSRodney W. Grimes */ 135df8bae1dSRodney W. Grimes vm_page_lock_queues(); 136df8bae1dSRodney W. Grimes 137df8bae1dSRodney W. Grimes /* 138df8bae1dSRodney W. Grimes * Start scanning the inactive queue for pages we can free. 139df8bae1dSRodney W. Grimes * We keep scanning until we have enough free pages or 140df8bae1dSRodney W. Grimes * we have scanned through the entire queue. If we 141df8bae1dSRodney W. Grimes * encounter dirty pages, we start cleaning them. 142df8bae1dSRodney W. Grimes */ 143df8bae1dSRodney W. Grimes 144df8bae1dSRodney W. Grimes pages_freed = 0; 145df8bae1dSRodney W. Grimes for (m = vm_page_queue_inactive.tqh_first; m != NULL; m = next) { 146df8bae1dSRodney W. Grimes s = splimp(); 147df8bae1dSRodney W. Grimes simple_lock(&vm_page_queue_free_lock); 148df8bae1dSRodney W. Grimes free = cnt.v_free_count; 149df8bae1dSRodney W. Grimes simple_unlock(&vm_page_queue_free_lock); 150df8bae1dSRodney W. Grimes splx(s); 151df8bae1dSRodney W. Grimes if (free >= cnt.v_free_target) 152df8bae1dSRodney W. Grimes break; 153df8bae1dSRodney W. Grimes 154df8bae1dSRodney W. Grimes cnt.v_scan++; 155df8bae1dSRodney W. Grimes next = m->pageq.tqe_next; 156df8bae1dSRodney W. Grimes 157df8bae1dSRodney W. Grimes /* 158df8bae1dSRodney W. Grimes * If the page has been referenced, move it back to the 159df8bae1dSRodney W. Grimes * active queue. 160df8bae1dSRodney W. Grimes */ 161df8bae1dSRodney W. Grimes if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { 162df8bae1dSRodney W. Grimes vm_page_activate(m); 163df8bae1dSRodney W. Grimes cnt.v_reactivated++; 164df8bae1dSRodney W. Grimes continue; 165df8bae1dSRodney W. Grimes } 166df8bae1dSRodney W. Grimes 167df8bae1dSRodney W. Grimes /* 168df8bae1dSRodney W. Grimes * If the page is clean, free it up. 169df8bae1dSRodney W. Grimes */ 170df8bae1dSRodney W. Grimes if (m->flags & PG_CLEAN) { 171df8bae1dSRodney W. Grimes object = m->object; 172df8bae1dSRodney W. Grimes if (vm_object_lock_try(object)) { 173df8bae1dSRodney W. Grimes pmap_page_protect(VM_PAGE_TO_PHYS(m), 174df8bae1dSRodney W. Grimes VM_PROT_NONE); 175df8bae1dSRodney W. Grimes vm_page_free(m); 176df8bae1dSRodney W. Grimes pages_freed++; 177df8bae1dSRodney W. Grimes cnt.v_dfree++; 178df8bae1dSRodney W. Grimes vm_object_unlock(object); 179df8bae1dSRodney W. Grimes } 180df8bae1dSRodney W. Grimes continue; 181df8bae1dSRodney W. Grimes } 182df8bae1dSRodney W. Grimes 183df8bae1dSRodney W. Grimes /* 184df8bae1dSRodney W. Grimes * If the page is dirty but already being washed, skip it. 185df8bae1dSRodney W. Grimes */ 186df8bae1dSRodney W. Grimes if ((m->flags & PG_LAUNDRY) == 0) 187df8bae1dSRodney W. Grimes continue; 188df8bae1dSRodney W. Grimes 189df8bae1dSRodney W. Grimes /* 190df8bae1dSRodney W. Grimes * Otherwise the page is dirty and still in the laundry, 191df8bae1dSRodney W. Grimes * so we start the cleaning operation and remove it from 192df8bae1dSRodney W. Grimes * the laundry. 193df8bae1dSRodney W. Grimes */ 194df8bae1dSRodney W. Grimes object = m->object; 195df8bae1dSRodney W. Grimes if (!vm_object_lock_try(object)) 196df8bae1dSRodney W. Grimes continue; 197df8bae1dSRodney W. Grimes cnt.v_pageouts++; 198df8bae1dSRodney W. Grimes #ifdef CLUSTERED_PAGEOUT 199df8bae1dSRodney W. Grimes if (object->pager && 200df8bae1dSRodney W. Grimes vm_pager_cancluster(object->pager, PG_CLUSTERPUT)) 201df8bae1dSRodney W. Grimes vm_pageout_cluster(m, object); 202df8bae1dSRodney W. Grimes else 203df8bae1dSRodney W. Grimes #endif 204df8bae1dSRodney W. Grimes vm_pageout_page(m, object); 205df8bae1dSRodney W. Grimes thread_wakeup((int) object); 206df8bae1dSRodney W. Grimes vm_object_unlock(object); 207df8bae1dSRodney W. Grimes /* 208df8bae1dSRodney W. Grimes * Former next page may no longer even be on the inactive 209df8bae1dSRodney W. Grimes * queue (due to potential blocking in the pager with the 210df8bae1dSRodney W. Grimes * queues unlocked). If it isn't, we just start over. 211df8bae1dSRodney W. Grimes */ 212df8bae1dSRodney W. Grimes if (next && (next->flags & PG_INACTIVE) == 0) 213df8bae1dSRodney W. Grimes next = vm_page_queue_inactive.tqh_first; 214df8bae1dSRodney W. Grimes } 215df8bae1dSRodney W. Grimes 216df8bae1dSRodney W. Grimes /* 217df8bae1dSRodney W. Grimes * Compute the page shortage. If we are still very low on memory 218df8bae1dSRodney W. Grimes * be sure that we will move a minimal amount of pages from active 219df8bae1dSRodney W. Grimes * to inactive. 220df8bae1dSRodney W. Grimes */ 221df8bae1dSRodney W. Grimes 222df8bae1dSRodney W. Grimes page_shortage = cnt.v_inactive_target - cnt.v_inactive_count; 223df8bae1dSRodney W. Grimes if (page_shortage <= 0 && pages_freed == 0) 224df8bae1dSRodney W. Grimes page_shortage = 1; 225df8bae1dSRodney W. Grimes 226df8bae1dSRodney W. Grimes while (page_shortage > 0) { 227df8bae1dSRodney W. Grimes /* 228df8bae1dSRodney W. Grimes * Move some more pages from active to inactive. 229df8bae1dSRodney W. Grimes */ 230df8bae1dSRodney W. Grimes 231df8bae1dSRodney W. Grimes if ((m = vm_page_queue_active.tqh_first) == NULL) 232df8bae1dSRodney W. Grimes break; 233df8bae1dSRodney W. Grimes vm_page_deactivate(m); 234df8bae1dSRodney W. Grimes page_shortage--; 235df8bae1dSRodney W. Grimes } 236df8bae1dSRodney W. Grimes 237df8bae1dSRodney W. Grimes vm_page_unlock_queues(); 238df8bae1dSRodney W. Grimes } 239df8bae1dSRodney W. Grimes 240df8bae1dSRodney W. Grimes /* 241df8bae1dSRodney W. Grimes * Called with object and page queues locked. 242df8bae1dSRodney W. Grimes * If reactivate is TRUE, a pager error causes the page to be 243df8bae1dSRodney W. Grimes * put back on the active queue, ow it is left on the inactive queue. 244df8bae1dSRodney W. Grimes */ 245df8bae1dSRodney W. Grimes void 246df8bae1dSRodney W. Grimes vm_pageout_page(m, object) 247df8bae1dSRodney W. Grimes vm_page_t m; 248df8bae1dSRodney W. Grimes vm_object_t object; 249df8bae1dSRodney W. Grimes { 250df8bae1dSRodney W. Grimes vm_pager_t pager; 251df8bae1dSRodney W. Grimes int pageout_status; 252df8bae1dSRodney W. Grimes 253df8bae1dSRodney W. Grimes /* 254df8bae1dSRodney W. Grimes * We set the busy bit to cause potential page faults on 255df8bae1dSRodney W. Grimes * this page to block. 256df8bae1dSRodney W. Grimes * 257df8bae1dSRodney W. Grimes * We also set pageout-in-progress to keep the object from 258df8bae1dSRodney W. Grimes * disappearing during pageout. This guarantees that the 259df8bae1dSRodney W. Grimes * page won't move from the inactive queue. (However, any 260df8bae1dSRodney W. Grimes * other page on the inactive queue may move!) 261df8bae1dSRodney W. Grimes */ 262df8bae1dSRodney W. Grimes pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE); 263df8bae1dSRodney W. Grimes m->flags |= PG_BUSY; 264df8bae1dSRodney W. Grimes 265df8bae1dSRodney W. Grimes /* 266df8bae1dSRodney W. Grimes * Try to collapse the object before making a pager for it. 267df8bae1dSRodney W. Grimes * We must unlock the page queues first. 268df8bae1dSRodney W. Grimes */ 269df8bae1dSRodney W. Grimes vm_page_unlock_queues(); 270df8bae1dSRodney W. Grimes if (object->pager == NULL) 271df8bae1dSRodney W. Grimes vm_object_collapse(object); 272df8bae1dSRodney W. Grimes 273df8bae1dSRodney W. Grimes object->paging_in_progress++; 274df8bae1dSRodney W. Grimes vm_object_unlock(object); 275df8bae1dSRodney W. Grimes 276df8bae1dSRodney W. Grimes /* 277df8bae1dSRodney W. Grimes * Do a wakeup here in case the following operations block. 278df8bae1dSRodney W. Grimes */ 279df8bae1dSRodney W. Grimes thread_wakeup((int) &cnt.v_free_count); 280df8bae1dSRodney W. Grimes 281df8bae1dSRodney W. Grimes /* 282df8bae1dSRodney W. Grimes * If there is no pager for the page, use the default pager. 283df8bae1dSRodney W. Grimes * If there is no place to put the page at the moment, 284df8bae1dSRodney W. Grimes * leave it in the laundry and hope that there will be 285df8bae1dSRodney W. Grimes * paging space later. 286df8bae1dSRodney W. Grimes */ 287df8bae1dSRodney W. Grimes if ((pager = object->pager) == NULL) { 288df8bae1dSRodney W. Grimes pager = vm_pager_allocate(PG_DFLT, (caddr_t)0, object->size, 289df8bae1dSRodney W. Grimes VM_PROT_ALL, (vm_offset_t)0); 290df8bae1dSRodney W. Grimes if (pager != NULL) 291df8bae1dSRodney W. Grimes vm_object_setpager(object, pager, 0, FALSE); 292df8bae1dSRodney W. Grimes } 293df8bae1dSRodney W. Grimes pageout_status = pager ? vm_pager_put(pager, m, FALSE) : VM_PAGER_FAIL; 294df8bae1dSRodney W. Grimes vm_object_lock(object); 295df8bae1dSRodney W. Grimes vm_page_lock_queues(); 296df8bae1dSRodney W. Grimes 297df8bae1dSRodney W. Grimes switch (pageout_status) { 298df8bae1dSRodney W. Grimes case VM_PAGER_OK: 299df8bae1dSRodney W. Grimes case VM_PAGER_PEND: 300df8bae1dSRodney W. Grimes cnt.v_pgpgout++; 301df8bae1dSRodney W. Grimes m->flags &= ~PG_LAUNDRY; 302df8bae1dSRodney W. Grimes break; 303df8bae1dSRodney W. Grimes case VM_PAGER_BAD: 304df8bae1dSRodney W. Grimes /* 305df8bae1dSRodney W. Grimes * Page outside of range of object. Right now we 306df8bae1dSRodney W. Grimes * essentially lose the changes by pretending it 307df8bae1dSRodney W. Grimes * worked. 308df8bae1dSRodney W. Grimes * 309df8bae1dSRodney W. Grimes * XXX dubious, what should we do? 310df8bae1dSRodney W. Grimes */ 311df8bae1dSRodney W. Grimes m->flags &= ~PG_LAUNDRY; 312df8bae1dSRodney W. Grimes m->flags |= PG_CLEAN; 313df8bae1dSRodney W. Grimes pmap_clear_modify(VM_PAGE_TO_PHYS(m)); 314df8bae1dSRodney W. Grimes break; 315df8bae1dSRodney W. Grimes case VM_PAGER_AGAIN: 316df8bae1dSRodney W. Grimes { 317df8bae1dSRodney W. Grimes extern int lbolt; 318df8bae1dSRodney W. Grimes 319df8bae1dSRodney W. Grimes /* 320df8bae1dSRodney W. Grimes * FAIL on a write is interpreted to mean a resource 321df8bae1dSRodney W. Grimes * shortage, so we put pause for awhile and try again. 322df8bae1dSRodney W. Grimes * XXX could get stuck here. 323df8bae1dSRodney W. Grimes */ 324df8bae1dSRodney W. Grimes (void) tsleep((caddr_t)&lbolt, PZERO|PCATCH, "pageout", 0); 325df8bae1dSRodney W. Grimes break; 326df8bae1dSRodney W. Grimes } 327df8bae1dSRodney W. Grimes case VM_PAGER_FAIL: 328df8bae1dSRodney W. Grimes case VM_PAGER_ERROR: 329df8bae1dSRodney W. Grimes /* 330df8bae1dSRodney W. Grimes * If page couldn't be paged out, then reactivate 331df8bae1dSRodney W. Grimes * the page so it doesn't clog the inactive list. 332df8bae1dSRodney W. Grimes * (We will try paging out it again later). 333df8bae1dSRodney W. Grimes */ 334df8bae1dSRodney W. Grimes vm_page_activate(m); 335df8bae1dSRodney W. Grimes cnt.v_reactivated++; 336df8bae1dSRodney W. Grimes break; 337df8bae1dSRodney W. Grimes } 338df8bae1dSRodney W. Grimes 339df8bae1dSRodney W. Grimes pmap_clear_reference(VM_PAGE_TO_PHYS(m)); 340df8bae1dSRodney W. Grimes 341df8bae1dSRodney W. Grimes /* 342df8bae1dSRodney W. Grimes * If the operation is still going, leave the page busy 343df8bae1dSRodney W. Grimes * to block all other accesses. Also, leave the paging 344df8bae1dSRodney W. Grimes * in progress indicator set so that we don't attempt an 345df8bae1dSRodney W. Grimes * object collapse. 346df8bae1dSRodney W. Grimes */ 347df8bae1dSRodney W. Grimes if (pageout_status != VM_PAGER_PEND) { 348df8bae1dSRodney W. Grimes m->flags &= ~PG_BUSY; 349df8bae1dSRodney W. Grimes PAGE_WAKEUP(m); 350df8bae1dSRodney W. Grimes object->paging_in_progress--; 351df8bae1dSRodney W. Grimes } 352df8bae1dSRodney W. Grimes } 353df8bae1dSRodney W. Grimes 354df8bae1dSRodney W. Grimes #ifdef CLUSTERED_PAGEOUT 355df8bae1dSRodney W. Grimes #define PAGEOUTABLE(p) \ 356df8bae1dSRodney W. Grimes ((((p)->flags & (PG_INACTIVE|PG_CLEAN|PG_LAUNDRY)) == \ 357df8bae1dSRodney W. Grimes (PG_INACTIVE|PG_LAUNDRY)) && !pmap_is_referenced(VM_PAGE_TO_PHYS(p))) 358df8bae1dSRodney W. Grimes 359df8bae1dSRodney W. Grimes /* 360df8bae1dSRodney W. Grimes * Attempt to pageout as many contiguous (to ``m'') dirty pages as possible 361df8bae1dSRodney W. Grimes * from ``object''. Using information returned from the pager, we assemble 362df8bae1dSRodney W. Grimes * a sorted list of contiguous dirty pages and feed them to the pager in one 363df8bae1dSRodney W. Grimes * chunk. Called with paging queues and object locked. Also, object must 364df8bae1dSRodney W. Grimes * already have a pager. 365df8bae1dSRodney W. Grimes */ 366df8bae1dSRodney W. Grimes void 367df8bae1dSRodney W. Grimes vm_pageout_cluster(m, object) 368df8bae1dSRodney W. Grimes vm_page_t m; 369df8bae1dSRodney W. Grimes vm_object_t object; 370df8bae1dSRodney W. Grimes { 371df8bae1dSRodney W. Grimes vm_offset_t offset, loff, hoff; 372df8bae1dSRodney W. Grimes vm_page_t plist[MAXPOCLUSTER], *plistp, p; 373df8bae1dSRodney W. Grimes int postatus, ix, count; 374df8bae1dSRodney W. Grimes 375df8bae1dSRodney W. Grimes /* 376df8bae1dSRodney W. Grimes * Determine the range of pages that can be part of a cluster 377df8bae1dSRodney W. Grimes * for this object/offset. If it is only our single page, just 378df8bae1dSRodney W. Grimes * do it normally. 379df8bae1dSRodney W. Grimes */ 380df8bae1dSRodney W. Grimes vm_pager_cluster(object->pager, m->offset, &loff, &hoff); 381df8bae1dSRodney W. Grimes if (hoff - loff == PAGE_SIZE) { 382df8bae1dSRodney W. Grimes vm_pageout_page(m, object); 383df8bae1dSRodney W. Grimes return; 384df8bae1dSRodney W. Grimes } 385df8bae1dSRodney W. Grimes 386df8bae1dSRodney W. Grimes plistp = plist; 387df8bae1dSRodney W. Grimes 388df8bae1dSRodney W. Grimes /* 389df8bae1dSRodney W. Grimes * Target page is always part of the cluster. 390df8bae1dSRodney W. Grimes */ 391df8bae1dSRodney W. Grimes pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE); 392df8bae1dSRodney W. Grimes m->flags |= PG_BUSY; 393df8bae1dSRodney W. Grimes plistp[atop(m->offset - loff)] = m; 394df8bae1dSRodney W. Grimes count = 1; 395df8bae1dSRodney W. Grimes 396df8bae1dSRodney W. Grimes /* 397df8bae1dSRodney W. Grimes * Backup from the given page til we find one not fulfilling 398df8bae1dSRodney W. Grimes * the pageout criteria or we hit the lower bound for the 399df8bae1dSRodney W. Grimes * cluster. For each page determined to be part of the 400df8bae1dSRodney W. Grimes * cluster, unmap it and busy it out so it won't change. 401df8bae1dSRodney W. Grimes */ 402df8bae1dSRodney W. Grimes ix = atop(m->offset - loff); 403df8bae1dSRodney W. Grimes offset = m->offset; 404df8bae1dSRodney W. Grimes while (offset > loff && count < MAXPOCLUSTER-1) { 405df8bae1dSRodney W. Grimes p = vm_page_lookup(object, offset - PAGE_SIZE); 406df8bae1dSRodney W. Grimes if (p == NULL || !PAGEOUTABLE(p)) 407df8bae1dSRodney W. Grimes break; 408df8bae1dSRodney W. Grimes pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_NONE); 409df8bae1dSRodney W. Grimes p->flags |= PG_BUSY; 410df8bae1dSRodney W. Grimes plistp[--ix] = p; 411df8bae1dSRodney W. Grimes offset -= PAGE_SIZE; 412df8bae1dSRodney W. Grimes count++; 413df8bae1dSRodney W. Grimes } 414df8bae1dSRodney W. Grimes plistp += atop(offset - loff); 415df8bae1dSRodney W. Grimes loff = offset; 416df8bae1dSRodney W. Grimes 417df8bae1dSRodney W. Grimes /* 418df8bae1dSRodney W. Grimes * Now do the same moving forward from the target. 419df8bae1dSRodney W. Grimes */ 420df8bae1dSRodney W. Grimes ix = atop(m->offset - loff) + 1; 421df8bae1dSRodney W. Grimes offset = m->offset + PAGE_SIZE; 422df8bae1dSRodney W. Grimes while (offset < hoff && count < MAXPOCLUSTER) { 423df8bae1dSRodney W. Grimes p = vm_page_lookup(object, offset); 424df8bae1dSRodney W. Grimes if (p == NULL || !PAGEOUTABLE(p)) 425df8bae1dSRodney W. Grimes break; 426df8bae1dSRodney W. Grimes pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_NONE); 427df8bae1dSRodney W. Grimes p->flags |= PG_BUSY; 428df8bae1dSRodney W. Grimes plistp[ix++] = p; 429df8bae1dSRodney W. Grimes offset += PAGE_SIZE; 430df8bae1dSRodney W. Grimes count++; 431df8bae1dSRodney W. Grimes } 432df8bae1dSRodney W. Grimes hoff = offset; 433df8bae1dSRodney W. Grimes 434df8bae1dSRodney W. Grimes /* 435df8bae1dSRodney W. Grimes * Pageout the page. 436df8bae1dSRodney W. Grimes * Unlock everything and do a wakeup prior to the pager call 437df8bae1dSRodney W. Grimes * in case it blocks. 438df8bae1dSRodney W. Grimes */ 439df8bae1dSRodney W. Grimes vm_page_unlock_queues(); 440df8bae1dSRodney W. Grimes object->paging_in_progress++; 441df8bae1dSRodney W. Grimes vm_object_unlock(object); 442df8bae1dSRodney W. Grimes again: 443df8bae1dSRodney W. Grimes thread_wakeup((int) &cnt.v_free_count); 444df8bae1dSRodney W. Grimes postatus = vm_pager_put_pages(object->pager, plistp, count, FALSE); 445df8bae1dSRodney W. Grimes /* 446df8bae1dSRodney W. Grimes * XXX rethink this 447df8bae1dSRodney W. Grimes */ 448df8bae1dSRodney W. Grimes if (postatus == VM_PAGER_AGAIN) { 449df8bae1dSRodney W. Grimes extern int lbolt; 450df8bae1dSRodney W. Grimes 451df8bae1dSRodney W. Grimes (void) tsleep((caddr_t)&lbolt, PZERO|PCATCH, "pageout", 0); 452df8bae1dSRodney W. Grimes goto again; 453df8bae1dSRodney W. Grimes } else if (postatus == VM_PAGER_BAD) 454df8bae1dSRodney W. Grimes panic("vm_pageout_cluster: VM_PAGER_BAD"); 455df8bae1dSRodney W. Grimes vm_object_lock(object); 456df8bae1dSRodney W. Grimes vm_page_lock_queues(); 457df8bae1dSRodney W. Grimes 458df8bae1dSRodney W. Grimes /* 459df8bae1dSRodney W. Grimes * Loop through the affected pages, reflecting the outcome of 460df8bae1dSRodney W. Grimes * the operation. 461df8bae1dSRodney W. Grimes */ 462df8bae1dSRodney W. Grimes for (ix = 0; ix < count; ix++) { 463df8bae1dSRodney W. Grimes p = *plistp++; 464df8bae1dSRodney W. Grimes switch (postatus) { 465df8bae1dSRodney W. Grimes case VM_PAGER_OK: 466df8bae1dSRodney W. Grimes case VM_PAGER_PEND: 467df8bae1dSRodney W. Grimes cnt.v_pgpgout++; 468df8bae1dSRodney W. Grimes p->flags &= ~PG_LAUNDRY; 469df8bae1dSRodney W. Grimes break; 470df8bae1dSRodney W. Grimes case VM_PAGER_FAIL: 471df8bae1dSRodney W. Grimes case VM_PAGER_ERROR: 472df8bae1dSRodney W. Grimes /* 473df8bae1dSRodney W. Grimes * Pageout failed, reactivate the target page so it 474df8bae1dSRodney W. Grimes * doesn't clog the inactive list. Other pages are 475df8bae1dSRodney W. Grimes * left as they are. 476df8bae1dSRodney W. Grimes */ 477df8bae1dSRodney W. Grimes if (p == m) { 478df8bae1dSRodney W. Grimes vm_page_activate(p); 479df8bae1dSRodney W. Grimes cnt.v_reactivated++; 480df8bae1dSRodney W. Grimes } 481df8bae1dSRodney W. Grimes break; 482df8bae1dSRodney W. Grimes } 483df8bae1dSRodney W. Grimes pmap_clear_reference(VM_PAGE_TO_PHYS(p)); 484df8bae1dSRodney W. Grimes /* 485df8bae1dSRodney W. Grimes * If the operation is still going, leave the page busy 486df8bae1dSRodney W. Grimes * to block all other accesses. 487df8bae1dSRodney W. Grimes */ 488df8bae1dSRodney W. Grimes if (postatus != VM_PAGER_PEND) { 489df8bae1dSRodney W. Grimes p->flags &= ~PG_BUSY; 490df8bae1dSRodney W. Grimes PAGE_WAKEUP(p); 491df8bae1dSRodney W. Grimes 492df8bae1dSRodney W. Grimes } 493df8bae1dSRodney W. Grimes } 494df8bae1dSRodney W. Grimes /* 495df8bae1dSRodney W. Grimes * If the operation is still going, leave the paging in progress 496df8bae1dSRodney W. Grimes * indicator set so that we don't attempt an object collapse. 497df8bae1dSRodney W. Grimes */ 498df8bae1dSRodney W. Grimes if (postatus != VM_PAGER_PEND) 499df8bae1dSRodney W. Grimes object->paging_in_progress--; 500df8bae1dSRodney W. Grimes 501df8bae1dSRodney W. Grimes } 502df8bae1dSRodney W. Grimes #endif 503df8bae1dSRodney W. Grimes 504df8bae1dSRodney W. Grimes /* 505df8bae1dSRodney W. Grimes * vm_pageout is the high level pageout daemon. 506df8bae1dSRodney W. Grimes */ 507df8bae1dSRodney W. Grimes 508df8bae1dSRodney W. Grimes void vm_pageout() 509df8bae1dSRodney W. Grimes { 510df8bae1dSRodney W. Grimes (void) spl0(); 511df8bae1dSRodney W. Grimes 512df8bae1dSRodney W. Grimes /* 513df8bae1dSRodney W. Grimes * Initialize some paging parameters. 514df8bae1dSRodney W. Grimes */ 515df8bae1dSRodney W. Grimes 516df8bae1dSRodney W. Grimes if (cnt.v_free_min == 0) { 517df8bae1dSRodney W. Grimes cnt.v_free_min = VM_PAGE_FREE_MIN; 518df8bae1dSRodney W. Grimes vm_page_free_min_min /= cnt.v_page_size; 519df8bae1dSRodney W. Grimes vm_page_free_min_max /= cnt.v_page_size; 520df8bae1dSRodney W. Grimes if (cnt.v_free_min < vm_page_free_min_min) 521df8bae1dSRodney W. Grimes cnt.v_free_min = vm_page_free_min_min; 522df8bae1dSRodney W. Grimes if (cnt.v_free_min > vm_page_free_min_max) 523df8bae1dSRodney W. Grimes cnt.v_free_min = vm_page_free_min_max; 524df8bae1dSRodney W. Grimes } 525df8bae1dSRodney W. Grimes 526df8bae1dSRodney W. Grimes if (cnt.v_free_target == 0) 527df8bae1dSRodney W. Grimes cnt.v_free_target = VM_PAGE_FREE_TARGET; 528df8bae1dSRodney W. Grimes 529df8bae1dSRodney W. Grimes if (cnt.v_free_target <= cnt.v_free_min) 530df8bae1dSRodney W. Grimes cnt.v_free_target = cnt.v_free_min + 1; 531df8bae1dSRodney W. Grimes 532df8bae1dSRodney W. Grimes /* XXX does not really belong here */ 533df8bae1dSRodney W. Grimes if (vm_page_max_wired == 0) 534df8bae1dSRodney W. Grimes vm_page_max_wired = cnt.v_free_count / 3; 535df8bae1dSRodney W. Grimes 536df8bae1dSRodney W. Grimes /* 537df8bae1dSRodney W. Grimes * The pageout daemon is never done, so loop 538df8bae1dSRodney W. Grimes * forever. 539df8bae1dSRodney W. Grimes */ 540df8bae1dSRodney W. Grimes 541df8bae1dSRodney W. Grimes simple_lock(&vm_pages_needed_lock); 542df8bae1dSRodney W. Grimes while (TRUE) { 543df8bae1dSRodney W. Grimes thread_sleep((int) &vm_pages_needed, &vm_pages_needed_lock, 544df8bae1dSRodney W. Grimes FALSE); 545df8bae1dSRodney W. Grimes /* 546df8bae1dSRodney W. Grimes * Compute the inactive target for this scan. 547df8bae1dSRodney W. Grimes * We need to keep a reasonable amount of memory in the 548df8bae1dSRodney W. Grimes * inactive list to better simulate LRU behavior. 549df8bae1dSRodney W. Grimes */ 550df8bae1dSRodney W. Grimes cnt.v_inactive_target = 551df8bae1dSRodney W. Grimes (cnt.v_active_count + cnt.v_inactive_count) / 3; 552df8bae1dSRodney W. Grimes if (cnt.v_inactive_target <= cnt.v_free_target) 553df8bae1dSRodney W. Grimes cnt.v_inactive_target = cnt.v_free_target + 1; 554df8bae1dSRodney W. Grimes 555df8bae1dSRodney W. Grimes /* 556df8bae1dSRodney W. Grimes * Only make a scan if we are likely to do something. 557df8bae1dSRodney W. Grimes * Otherwise we might have been awakened by a pager 558df8bae1dSRodney W. Grimes * to clean up async pageouts. 559df8bae1dSRodney W. Grimes */ 560df8bae1dSRodney W. Grimes if (cnt.v_free_count < cnt.v_free_target || 561df8bae1dSRodney W. Grimes cnt.v_inactive_count < cnt.v_inactive_target) 562df8bae1dSRodney W. Grimes vm_pageout_scan(); 563df8bae1dSRodney W. Grimes vm_pager_sync(); 564df8bae1dSRodney W. Grimes simple_lock(&vm_pages_needed_lock); 565df8bae1dSRodney W. Grimes thread_wakeup((int) &cnt.v_free_count); 566df8bae1dSRodney W. Grimes } 567df8bae1dSRodney W. Grimes } 568