diff options
Diffstat (limited to 'sys/vm/vm_pageout.c')
-rw-r--r-- | sys/vm/vm_pageout.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c new file mode 100644 index 00000000000..d4456e6009a --- /dev/null +++ b/sys/vm/vm_pageout.c @@ -0,0 +1,571 @@ +/* $NetBSD: vm_pageout.c,v 1.22 1995/06/28 02:58:51 cgd Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * The Mach Operating System project at Carnegie-Mellon University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vm_pageout.c 8.5 (Berkeley) 2/14/94 + * + * + * Copyright (c) 1987, 1990 Carnegie-Mellon University. + * All rights reserved. + * + * Authors: Avadis Tevanian, Jr., Michael Wayne Young + * + * Permission to use, copy, modify and distribute this software and + * its documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND + * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie the + * rights to redistribute these changes. + */ + +/* + * The proverbial page-out daemon. + */ + +#include <sys/param.h> + +#include <vm/vm.h> +#include <vm/vm_page.h> +#include <vm/vm_pageout.h> + +#ifndef VM_PAGE_FREE_MIN +#define VM_PAGE_FREE_MIN (cnt.v_free_count / 20) +#endif + +#ifndef VM_PAGE_FREE_TARGET +#define VM_PAGE_FREE_TARGET ((cnt.v_free_min * 4) / 3) +#endif + +int vm_page_free_min_min = 16 * 1024; +int vm_page_free_min_max = 256 * 1024; + +int vm_pages_needed; /* Event on which pageout daemon sleeps */ + +int vm_page_max_wired = 0; /* XXX max # of wired pages system-wide */ + +#ifdef CLUSTERED_PAGEOUT +#define MAXPOCLUSTER (MAXPHYS/NBPG) /* XXX */ +int doclustered_pageout = 1; +#endif + +/* + * vm_pageout_scan does the dirty work for the pageout daemon. + */ +void +vm_pageout_scan() +{ + register vm_page_t m, next; + register int page_shortage; + register int s; + register int pages_freed; + int free; + vm_object_t object; + + /* + * Only continue when we want more pages to be "free" + */ + + cnt.v_rev++; + + s = splimp(); + simple_lock(&vm_page_queue_free_lock); + free = cnt.v_free_count; + simple_unlock(&vm_page_queue_free_lock); + splx(s); + +#ifndef __SWAP_BROKEN /* XXX */ + if (free < cnt.v_free_target) { + swapout_threads(); + + /* + * Be sure the pmap system is updated so + * we can scan the inactive queue. + */ + + pmap_update(); + } +#endif /* XXX */ + + /* + * Acquire the resident page system lock, + * as we may be changing what's resident quite a bit. + */ + vm_page_lock_queues(); + + /* + * Start scanning the inactive queue for pages we can free. + * We keep scanning until we have enough free pages or + * we have scanned through the entire queue. If we + * encounter dirty pages, we start cleaning them. + */ + + pages_freed = 0; + for (m = vm_page_queue_inactive.tqh_first; m != NULL; m = next) { + s = splimp(); + simple_lock(&vm_page_queue_free_lock); + free = cnt.v_free_count; + simple_unlock(&vm_page_queue_free_lock); + splx(s); + if (free >= cnt.v_free_target) + break; + + cnt.v_scan++; + next = m->pageq.tqe_next; + + /* + * If the page has been referenced, move it back to the + * active queue. + */ + if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { + vm_page_activate(m); + cnt.v_reactivated++; + continue; + } + + /* + * If the page is clean, free it up. + */ + if (m->flags & PG_CLEAN) { + object = m->object; + if (vm_object_lock_try(object)) { + pmap_page_protect(VM_PAGE_TO_PHYS(m), + VM_PROT_NONE); + vm_page_free(m); + pages_freed++; + cnt.v_dfree++; + vm_object_unlock(object); + } + continue; + } + + /* + * If the page is dirty but already being washed, skip it. + */ + if ((m->flags & PG_LAUNDRY) == 0) + continue; + + /* + * Otherwise the page is dirty and still in the laundry, + * so we start the cleaning operation and remove it from + * the laundry. + */ + object = m->object; + if (!vm_object_lock_try(object)) + continue; + cnt.v_pageouts++; +#ifdef CLUSTERED_PAGEOUT + if (object->pager && + vm_pager_cancluster(object->pager, PG_CLUSTERPUT)) + vm_pageout_cluster(m, object); + else +#endif + vm_pageout_page(m, object); + thread_wakeup(object); + vm_object_unlock(object); + /* + * Former next page may no longer even be on the inactive + * queue (due to potential blocking in the pager with the + * queues unlocked). If it isn't, we just start over. + */ + if (next && (next->flags & PG_INACTIVE) == 0) + next = vm_page_queue_inactive.tqh_first; + } + + /* + * Compute the page shortage. If we are still very low on memory + * be sure that we will move a minimal amount of pages from active + * to inactive. + */ + + page_shortage = cnt.v_inactive_target - cnt.v_inactive_count; + if (page_shortage <= 0 && pages_freed == 0) + page_shortage = 1; + + while (page_shortage > 0) { + /* + * Move some more pages from active to inactive. + */ + + if ((m = vm_page_queue_active.tqh_first) == NULL) + break; + vm_page_deactivate(m); + page_shortage--; + } + + vm_page_unlock_queues(); +} + +/* + * Called with object and page queues locked. + * If reactivate is TRUE, a pager error causes the page to be + * put back on the active queue, ow it is left on the inactive queue. + */ +void +vm_pageout_page(m, object) + vm_page_t m; + vm_object_t object; +{ + vm_pager_t pager; + int pageout_status; + + /* + * We set the busy bit to cause potential page faults on + * this page to block. + * + * We also set pageout-in-progress to keep the object from + * disappearing during pageout. This guarantees that the + * page won't move from the inactive queue. (However, any + * other page on the inactive queue may move!) + */ + pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE); + m->flags |= PG_BUSY; + + /* + * Try to collapse the object before making a pager for it. + * We must unlock the page queues first. + */ + vm_page_unlock_queues(); + if (object->pager == NULL) + vm_object_collapse(object); + + object->paging_in_progress++; + vm_object_unlock(object); + + /* + * Do a wakeup here in case the following operations block. + */ + thread_wakeup(&cnt.v_free_count); + + /* + * If there is no pager for the page, use the default pager. + * If there is no place to put the page at the moment, + * leave it in the laundry and hope that there will be + * paging space later. + */ + if ((pager = object->pager) == NULL) { + pager = vm_pager_allocate(PG_DFLT, (caddr_t)0, object->size, + VM_PROT_ALL, (vm_offset_t)0); + if (pager != NULL) + vm_object_setpager(object, pager, 0, FALSE); + } + pageout_status = pager ? vm_pager_put(pager, m, FALSE) : VM_PAGER_FAIL; + vm_object_lock(object); + vm_page_lock_queues(); + + switch (pageout_status) { + case VM_PAGER_OK: + case VM_PAGER_PEND: + cnt.v_pgpgout++; + m->flags &= ~PG_LAUNDRY; + break; + case VM_PAGER_BAD: + /* + * Page outside of range of object. Right now we + * essentially lose the changes by pretending it + * worked. + * + * XXX dubious, what should we do? + */ + m->flags &= ~PG_LAUNDRY; + m->flags |= PG_CLEAN; + pmap_clear_modify(VM_PAGE_TO_PHYS(m)); + break; + case VM_PAGER_AGAIN: + { + extern int lbolt; + + /* + * FAIL on a write is interpreted to mean a resource + * shortage, so we put pause for awhile and try again. + * XXX could get stuck here. + */ + (void) tsleep((caddr_t)&lbolt, PZERO|PCATCH, "pageout", 0); + break; + } + case VM_PAGER_FAIL: + case VM_PAGER_ERROR: + /* + * If page couldn't be paged out, then reactivate + * the page so it doesn't clog the inactive list. + * (We will try paging out it again later). + */ + vm_page_activate(m); + cnt.v_reactivated++; + break; + } + + pmap_clear_reference(VM_PAGE_TO_PHYS(m)); + + /* + * If the operation is still going, leave the page busy + * to block all other accesses. Also, leave the paging + * in progress indicator set so that we don't attempt an + * object collapse. + */ + if (pageout_status != VM_PAGER_PEND) { + m->flags &= ~PG_BUSY; + PAGE_WAKEUP(m); + object->paging_in_progress--; + } +} + +#ifdef CLUSTERED_PAGEOUT +#define PAGEOUTABLE(p) \ + ((((p)->flags & (PG_INACTIVE|PG_CLEAN|PG_LAUNDRY)) == \ + (PG_INACTIVE|PG_LAUNDRY)) && !pmap_is_referenced(VM_PAGE_TO_PHYS(p))) + +/* + * Attempt to pageout as many contiguous (to ``m'') dirty pages as possible + * from ``object''. Using information returned from the pager, we assemble + * a sorted list of contiguous dirty pages and feed them to the pager in one + * chunk. Called with paging queues and object locked. Also, object must + * already have a pager. + */ +void +vm_pageout_cluster(m, object) + vm_page_t m; + vm_object_t object; +{ + vm_offset_t offset, loff, hoff; + vm_page_t plist[MAXPOCLUSTER], *plistp, p; + int postatus, ix, count; + + /* + * Determine the range of pages that can be part of a cluster + * for this object/offset. If it is only our single page, just + * do it normally. + */ + vm_pager_cluster(object->pager, m->offset, &loff, &hoff); + if (hoff - loff == PAGE_SIZE) { + vm_pageout_page(m, object); + return; + } + + plistp = plist; + + /* + * Target page is always part of the cluster. + */ + pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE); + m->flags |= PG_BUSY; + plistp[atop(m->offset - loff)] = m; + count = 1; + + /* + * Backup from the given page til we find one not fulfilling + * the pageout criteria or we hit the lower bound for the + * cluster. For each page determined to be part of the + * cluster, unmap it and busy it out so it won't change. + */ + ix = atop(m->offset - loff); + offset = m->offset; + while (offset > loff && count < MAXPOCLUSTER-1) { + p = vm_page_lookup(object, offset - PAGE_SIZE); + if (p == NULL || !PAGEOUTABLE(p)) + break; + pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_NONE); + p->flags |= PG_BUSY; + plistp[--ix] = p; + offset -= PAGE_SIZE; + count++; + } + plistp += atop(offset - loff); + loff = offset; + + /* + * Now do the same moving forward from the target. + */ + ix = atop(m->offset - loff) + 1; + offset = m->offset + PAGE_SIZE; + while (offset < hoff && count < MAXPOCLUSTER) { + p = vm_page_lookup(object, offset); + if (p == NULL || !PAGEOUTABLE(p)) + break; + pmap_page_protect(VM_PAGE_TO_PHYS(p), VM_PROT_NONE); + p->flags |= PG_BUSY; + plistp[ix++] = p; + offset += PAGE_SIZE; + count++; + } + hoff = offset; + + /* + * Pageout the page. + * Unlock everything and do a wakeup prior to the pager call + * in case it blocks. + */ + vm_page_unlock_queues(); + object->paging_in_progress++; + vm_object_unlock(object); +again: + thread_wakeup(&cnt.v_free_count); + postatus = vm_pager_put_pages(object->pager, plistp, count, FALSE); + /* + * XXX rethink this + */ + if (postatus == VM_PAGER_AGAIN) { + extern int lbolt; + + (void) tsleep((caddr_t)&lbolt, PZERO|PCATCH, "pageout", 0); + goto again; + } else if (postatus == VM_PAGER_BAD) + panic("vm_pageout_cluster: VM_PAGER_BAD"); + vm_object_lock(object); + vm_page_lock_queues(); + + /* + * Loop through the affected pages, reflecting the outcome of + * the operation. + */ + for (ix = 0; ix < count; ix++) { + p = *plistp++; + switch (postatus) { + case VM_PAGER_OK: + case VM_PAGER_PEND: + cnt.v_pgpgout++; + p->flags &= ~PG_LAUNDRY; + break; + case VM_PAGER_FAIL: + case VM_PAGER_ERROR: + /* + * Pageout failed, reactivate the target page so it + * doesn't clog the inactive list. Other pages are + * left as they are. + */ + if (p == m) { + vm_page_activate(p); + cnt.v_reactivated++; + } + break; + } + pmap_clear_reference(VM_PAGE_TO_PHYS(p)); + /* + * If the operation is still going, leave the page busy + * to block all other accesses. + */ + if (postatus != VM_PAGER_PEND) { + p->flags &= ~PG_BUSY; + PAGE_WAKEUP(p); + + } + } + /* + * If the operation is still going, leave the paging in progress + * indicator set so that we don't attempt an object collapse. + */ + if (postatus != VM_PAGER_PEND) + object->paging_in_progress--; + +} +#endif + +/* + * vm_pageout is the high level pageout daemon. + */ + +void +vm_pageout() +{ + (void) spl0(); + + /* + * Initialize some paging parameters. + */ + + if (cnt.v_free_min == 0) { + cnt.v_free_min = VM_PAGE_FREE_MIN; + vm_page_free_min_min /= cnt.v_page_size; + vm_page_free_min_max /= cnt.v_page_size; + if (cnt.v_free_min < vm_page_free_min_min) + cnt.v_free_min = vm_page_free_min_min; + if (cnt.v_free_min > vm_page_free_min_max) + cnt.v_free_min = vm_page_free_min_max; + } + + if (cnt.v_free_target == 0) + cnt.v_free_target = VM_PAGE_FREE_TARGET; + + if (cnt.v_free_target <= cnt.v_free_min) + cnt.v_free_target = cnt.v_free_min + 1; + + /* XXX does not really belong here */ + if (vm_page_max_wired == 0) + vm_page_max_wired = cnt.v_free_count / 3; + + /* + * The pageout daemon is never done, so loop + * forever. + */ + + simple_lock(&vm_pages_needed_lock); + while (TRUE) { + thread_sleep(&vm_pages_needed, &vm_pages_needed_lock, FALSE); + /* + * Compute the inactive target for this scan. + * We need to keep a reasonable amount of memory in the + * inactive list to better simulate LRU behavior. + */ + cnt.v_inactive_target = + (cnt.v_active_count + cnt.v_inactive_count) / 3; + if (cnt.v_inactive_target <= cnt.v_free_target) + cnt.v_inactive_target = cnt.v_free_target + 1; + + /* + * Only make a scan if we are likely to do something. + * Otherwise we might have been awakened by a pager + * to clean up async pageouts. + */ + if (cnt.v_free_count < cnt.v_free_target || + cnt.v_inactive_count < cnt.v_inactive_target) + vm_pageout_scan(); + vm_pager_sync(); + simple_lock(&vm_pages_needed_lock); + thread_wakeup(&cnt.v_free_count); + } +} |