diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1994-05-25 09:21:21 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1994-05-25 09:21:21 +0000 |
commit | 2469c867a164210ce96143517059f21db7f1fd17 (patch) | |
tree | 9179427ac860211c445df663fd2b86267366bfba /sys/vm/vm_pageout.c | |
parent | cb0aba89af15a48e2655e898a503946ac4cb42ae (diff) | |
download | FreeBSD-src-2469c867a164210ce96143517059f21db7f1fd17.zip FreeBSD-src-2469c867a164210ce96143517059f21db7f1fd17.tar.gz |
The big 4.4BSD Lite to FreeBSD 2.0.0 (Development) patch.
Reviewed by: Rodney W. Grimes
Submitted by: John Dyson and David Greenman
Diffstat (limited to 'sys/vm/vm_pageout.c')
-rw-r--r-- | sys/vm/vm_pageout.c | 1063 |
1 files changed, 685 insertions, 378 deletions
diff --git a/sys/vm/vm_pageout.c b/sys/vm/vm_pageout.c index 6795405..202bf03 100644 --- a/sys/vm/vm_pageout.c +++ b/sys/vm/vm_pageout.c @@ -1,6 +1,10 @@ /* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. + * Copyright (c) 1991 Regents of the University of California. + * All rights reserved. + * Copyright (c) 1994 John S. Dyson + * All rights reserved. + * Copyright (c) 1994 David Greenman + * All rights reserved. * * This code is derived from software contributed to Berkeley by * The Mach Operating System project at Carnegie-Mellon University. @@ -33,7 +37,7 @@ * 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 + * @(#)vm_pageout.c 7.4 (Berkeley) 5/7/91 * * * Copyright (c) 1987, 1990 Carnegie-Mellon University. @@ -60,6 +64,8 @@ * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. + * + * $Id: vm_pageout.c,v 1.20 1994/04/20 07:07:15 davidg Exp $ */ /* @@ -67,501 +73,802 @@ */ #include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/resourcevar.h> +#include <sys/malloc.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 +extern vm_map_t kmem_map; +int vm_pages_needed; /* Event on which pageout daemon sleeps */ +int vm_pagescanner; /* Event on which pagescanner sleeps */ +int vm_pageout_free_min = 0; /* Stop pageout to wait for pagers at this free level */ + +int vm_pageout_pages_needed = 0; /* flag saying that the pageout daemon needs pages */ +int vm_page_pagesfreed; -#ifndef VM_PAGE_FREE_TARGET -#define VM_PAGE_FREE_TARGET ((cnt.v_free_min * 4) / 3) -#endif +extern int npendingio; +extern int hz; +int vm_pageout_proc_limit; +extern int nswiodone; +extern int swap_pager_full; +extern int swap_pager_ready(); -int vm_page_free_min_min = 16 * 1024; -int vm_page_free_min_max = 256 * 1024; +#define MAXREF 32767 -int vm_pages_needed; /* Event on which pageout daemon sleeps */ +#define MAXSCAN 512 /* maximum number of pages to scan in active queue */ + /* set the "clock" hands to be (MAXSCAN * 4096) Bytes */ +#define ACT_DECLINE 1 +#define ACT_ADVANCE 6 +#define ACT_MAX 300 + +#define LOWATER ((2048*1024)/NBPG) + +#define VM_PAGEOUT_PAGE_COUNT 8 +static vm_offset_t vm_space_needed; +int vm_pageout_req_do_stats; 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. + * vm_pageout_clean: + * cleans a vm_page */ -void -vm_pageout_scan() +int +vm_pageout_clean(m, sync) + register vm_page_t m; + int sync; { - register vm_page_t m, next; - register int page_shortage; - register int s; - register int pages_freed; - int free; - vm_object_t object; + /* + * Clean the page and remove it from the + * laundry. + * + * We set the busy bit to cause + * potential page faults on this page to + * block. + * + * And we 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!) + */ + + register vm_object_t object; + register vm_pager_t pager; + int pageout_status[VM_PAGEOUT_PAGE_COUNT]; + vm_page_t ms[VM_PAGEOUT_PAGE_COUNT]; + int pageout_count; + int anyok=0; + int i; + vm_offset_t offset = m->offset; + + object = m->object; + if (!object) { + printf("pager: object missing\n"); + return 0; + } /* - * Only continue when we want more pages to be "free" + * Try to collapse the object before + * making a pager for it. We must + * unlock the page queues first. + * We try to defer the creation of a pager + * until all shadows are not paging. This + * allows vm_object_collapse to work better and + * helps control swap space size. + * (J. Dyson 11 Nov 93) */ - cnt.v_rev++; + if (!object->pager && + cnt.v_free_count < vm_pageout_free_min) + return 0; - s = splimp(); - simple_lock(&vm_page_queue_free_lock); - free = cnt.v_free_count; - simple_unlock(&vm_page_queue_free_lock); - splx(s); + if (!object->pager && + object->shadow && + object->shadow->paging_in_progress) + return 0; - if (free < cnt.v_free_target) { - swapout_threads(); + if( !sync) { + if (object->shadow) { + vm_object_collapse(object); + if (!vm_page_lookup(object, offset)) + return 0; + } - /* - * Be sure the pmap system is updated so - * we can scan the inactive queue. - */ + if ((m->flags & PG_BUSY) || (m->hold_count != 0)) { + return 0; + } + } + + pageout_count = 1; + ms[0] = m; + + if( pager = object->pager) { + for(i=1;i<VM_PAGEOUT_PAGE_COUNT;i++) { + if( ms[i] = vm_page_lookup( object, offset+i*NBPG)) { + if((((ms[i]->flags & (PG_CLEAN|PG_INACTIVE|PG_BUSY)) == PG_INACTIVE) + || (( ms[i]->flags & PG_CLEAN) == 0 && sync == VM_PAGEOUT_FORCE)) + && (ms[i]->wire_count == 0) + && (ms[i]->hold_count == 0)) + pageout_count++; + else + break; + } else + break; + } + for(i=0;i<pageout_count;i++) { + ms[i]->flags |= PG_BUSY; + pmap_page_protect(VM_PAGE_TO_PHYS(ms[i]), VM_PROT_READ); + } + object->paging_in_progress += pageout_count; + cnt.v_pageouts += pageout_count; + } else { + + m->flags |= PG_BUSY; - pmap_update(); + pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_READ); + + cnt.v_pageouts++; + + object->paging_in_progress++; + + pager = vm_pager_allocate(PG_DFLT, (caddr_t)0, + object->size, VM_PROT_ALL, 0); + if (pager != NULL) { + vm_object_setpager(object, pager, 0, FALSE); + } } /* - * Acquire the resident page system lock, - * as we may be changing what's resident quite a bit. + * If there is no pager for the page, + * use the default pager. If there's + * no place to put the page at the + * moment, leave it in the laundry and + * hope that there will be paging space + * later. */ - 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. - */ + if ((pager && pager->pg_type == PG_SWAP) || + cnt.v_free_count >= vm_pageout_free_min) { + if( pageout_count == 1) { + pageout_status[0] = pager ? + vm_pager_put(pager, m, + ((sync || (object == kernel_object)) ? TRUE: FALSE)) : + VM_PAGER_FAIL; + } else { + if( !pager) { + for(i=0;i<pageout_count;i++) + pageout_status[i] = VM_PAGER_FAIL; + } else { + vm_pager_put_pages(pager, ms, pageout_count, + ((sync || (object == kernel_object)) ? TRUE : FALSE), + pageout_status); + } + } + + } else { + for(i=0;i<pageout_count;i++) + pageout_status[i] = VM_PAGER_FAIL; + } - 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) + for(i=0;i<pageout_count;i++) { + switch (pageout_status[i]) { + case VM_PAGER_OK: + ms[i]->flags &= ~PG_LAUNDRY; + ++anyok; + break; + case VM_PAGER_PEND: + ms[i]->flags &= ~PG_LAUNDRY; + ++anyok; + break; + case VM_PAGER_BAD: + /* + * Page outside of range of object. + * Right now we essentially lose the + * changes by pretending it worked. + */ + ms[i]->flags &= ~PG_LAUNDRY; + ms[i]->flags |= PG_CLEAN; + pmap_clear_modify(VM_PAGE_TO_PHYS(ms[i])); + break; + case VM_PAGER_ERROR: + case VM_PAGER_FAIL: + /* + * 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). + */ + if (ms[i]->flags & PG_INACTIVE) + vm_page_activate(ms[i]); + break; + case VM_PAGER_AGAIN: break; + } - cnt.v_scan++; - next = m->pageq.tqe_next; /* - * If the page has been referenced, move it back to the - * active queue. + * 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 (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { - vm_page_activate(m); - cnt.v_reactivated++; - continue; + if (pageout_status[i] != VM_PAGER_PEND) { + PAGE_WAKEUP(ms[i]); + if (--object->paging_in_progress == 0) + wakeup((caddr_t) object); + if (pmap_is_referenced(VM_PAGE_TO_PHYS(ms[i]))) { + pmap_clear_reference(VM_PAGE_TO_PHYS(ms[i])); + if( ms[i]->flags & PG_INACTIVE) + vm_page_activate(ms[i]); + } } + } + return anyok; +} +/* + * vm_pageout_object_deactivate_pages + * + * deactivate enough pages to satisfy the inactive target + * requirements or if vm_page_proc_limit is set, then + * deactivate all of the pages in the object and its + * shadows. + * + * The object and map must be locked. + */ +int +vm_pageout_object_deactivate_pages(map, object, count) + vm_map_t map; + vm_object_t object; + int count; +{ + register vm_page_t p, next; + int rcount; + int s; + int dcount; + + dcount = 0; + if (count == 0) + count = 1; + + if (object->shadow) { + int scount = count; + if( object->shadow->ref_count > 1) + scount /= object->shadow->ref_count; + if( scount) + dcount += vm_pageout_object_deactivate_pages(map, object->shadow, scount); + } + + if (object->paging_in_progress) + return dcount; + + /* + * scan the objects entire memory queue + */ + rcount = object->resident_page_count; + p = object->memq.tqh_first; + while (p && (rcount-- > 0)) { + next = p->listq.tqe_next; + vm_page_lock_queues(); /* - * If the page is clean, free it up. + * if a page is active, not wired and is in the processes pmap, + * then deactivate the page. */ - 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); + if ((p->flags & (PG_ACTIVE|PG_BUSY)) == PG_ACTIVE && + p->wire_count == 0 && + p->hold_count == 0 && + pmap_page_exists(vm_map_pmap(map), VM_PAGE_TO_PHYS(p))) { + if (!pmap_is_referenced(VM_PAGE_TO_PHYS(p))) { + p->act_count -= min(p->act_count, ACT_DECLINE); + /* + * if the page act_count is zero -- then we deactivate + */ + if (!p->act_count) { + vm_page_deactivate(p); + pmap_page_protect(VM_PAGE_TO_PHYS(p), + VM_PROT_NONE); + /* + * else if on the next go-around we will deactivate the page + * we need to place the page on the end of the queue to age + * the other pages in memory. + */ + } else { + TAILQ_REMOVE(&vm_page_queue_active, p, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_active, p, pageq); + TAILQ_REMOVE(&object->memq, p, listq); + TAILQ_INSERT_TAIL(&object->memq, p, listq); + } + /* + * see if we are done yet + */ + if (p->flags & PG_INACTIVE) { + --count; + ++dcount; + if (count <= 0 && + cnt.v_inactive_count > cnt.v_inactive_target) { + vm_page_unlock_queues(); + return dcount; + } + } + + } else { + /* + * Move the page to the bottom of the queue. + */ + pmap_clear_reference(VM_PAGE_TO_PHYS(p)); + if (p->act_count < ACT_MAX) + p->act_count += ACT_ADVANCE; + + TAILQ_REMOVE(&vm_page_queue_active, p, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_active, p, pageq); + TAILQ_REMOVE(&object->memq, p, listq); + TAILQ_INSERT_TAIL(&object->memq, p, listq); } - continue; } + vm_page_unlock_queues(); + p = next; + } + return dcount; +} + + +/* + * deactivate some number of pages in a map, try to do it fairly, but + * that is really hard to do. + */ + +void +vm_pageout_map_deactivate_pages(map, entry, count, freeer) + vm_map_t map; + vm_map_entry_t entry; + int *count; + int (*freeer)(vm_map_t, vm_object_t, int); +{ + vm_map_t tmpm; + vm_map_entry_t tmpe; + vm_object_t obj; + if (*count <= 0) + return; + vm_map_reference(map); + if (!lock_try_read(&map->lock)) { + vm_map_deallocate(map); + return; + } + if (entry == 0) { + tmpe = map->header.next; + while (tmpe != &map->header && *count > 0) { + vm_pageout_map_deactivate_pages(map, tmpe, count, freeer); + tmpe = tmpe->next; + }; + } else if (entry->is_sub_map || entry->is_a_map) { + tmpm = entry->object.share_map; + tmpe = tmpm->header.next; + while (tmpe != &tmpm->header && *count > 0) { + vm_pageout_map_deactivate_pages(tmpm, tmpe, count, freeer); + tmpe = tmpe->next; + }; + } else if (obj = entry->object.vm_object) { + *count -= (*freeer)(map, obj, *count); + } + lock_read_done(&map->lock); + vm_map_deallocate(map); + return; +} + +/* + * vm_pageout_scan does the dirty work for the pageout daemon. + */ +int +vm_pageout_scan() +{ + vm_page_t m; + int page_shortage, maxscan, maxlaunder; + int pages_freed, free, nproc; + int desired_free; + vm_page_t next; + struct proc *p; + vm_object_t object; + int s; + int force_wakeup = 0; + +morefree: + /* + * scan the processes for exceeding their rlimits or if process + * is swapped out -- deactivate pages + */ + +rescanproc1: + for (p = (struct proc *)allproc; p != NULL; p = p->p_next) { + vm_offset_t size; + int overage; + vm_offset_t limit; + /* - * If the page is dirty but already being washed, skip it. + * if this is a system process or if we have already + * looked at this process, skip it. */ - if ((m->flags & PG_LAUNDRY) == 0) + if (p->p_flag & (P_SYSTEM|P_WEXIT)) { continue; + } /* - * Otherwise the page is dirty and still in the laundry, - * so we start the cleaning operation and remove it from - * the laundry. + * if the process is in a non-running type state, + * don't touch it. */ - object = m->object; - if (!vm_object_lock_try(object)) + if (p->p_stat != SRUN && p->p_stat != SSLEEP) { 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((int) 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. + * get a limit */ - 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) { + limit = min(p->p_rlimit[RLIMIT_RSS].rlim_cur, + p->p_rlimit[RLIMIT_RSS].rlim_max); + /* - * Move some more pages from active to inactive. + * let processes that are swapped out really be swapped out + * set the limit to nothing (will force a swap-out.) */ + if ((p->p_flag & P_INMEM) == 0) + limit = 0; + + size = p->p_vmspace->vm_pmap.pm_stats.resident_count * NBPG; + if (size >= limit) { + overage = (size - limit) / NBPG; + vm_pageout_map_deactivate_pages(&p->p_vmspace->vm_map, + (vm_map_entry_t) 0, &overage, vm_pageout_object_deactivate_pages); + } - if ((m = vm_page_queue_active.tqh_first) == NULL) - break; - vm_page_deactivate(m); - page_shortage--; } - vm_page_unlock_queues(); -} + if (((cnt.v_free_count + cnt.v_inactive_count) >= + (cnt.v_inactive_target + cnt.v_free_target)) && + (cnt.v_free_count >= cnt.v_free_target)) + return force_wakeup; -/* - * 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; + pages_freed = 0; + desired_free = cnt.v_free_target; /* - * 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!) + * 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. */ - 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); + maxlaunder = (cnt.v_free_target - cnt.v_free_count); + maxscan = cnt.v_inactive_count; +rescan1: + m = vm_page_queue_inactive.tqh_first; + while (m && (maxscan-- > 0) && + (cnt.v_free_count < desired_free) ) { + vm_page_t next; - object->paging_in_progress++; - vm_object_unlock(object); + next = m->pageq.tqe_next; - /* - * Do a wakeup here in case the following operations block. - */ - thread_wakeup((int) &cnt.v_free_count); + if( (m->flags & PG_INACTIVE) == 0) { + printf("vm_pageout_scan: page not inactive?"); + continue; + } - /* - * 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? + * activate held pages */ - m->flags &= ~PG_LAUNDRY; - m->flags |= PG_CLEAN; - pmap_clear_modify(VM_PAGE_TO_PHYS(m)); - break; - case VM_PAGER_AGAIN: - { - extern int lbolt; + if (m->hold_count != 0) { + vm_page_activate(m); + m = next; + continue; + } /* - * 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. + * dont mess with busy pages */ - (void) tsleep((caddr_t)&lbolt, PZERO|PCATCH, "pageout", 0); - break; - } - case VM_PAGER_FAIL: - case VM_PAGER_ERROR: + if (m->flags & PG_BUSY) { + m = next; + continue; + } + /* - * 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). + * if page is clean and but the page has been referenced, + * then reactivate the page, but if we are very low on memory + * or the page has not been referenced, then we free it to the + * vm system. */ - vm_page_activate(m); - cnt.v_reactivated++; - break; - } + if (m->flags & PG_CLEAN) { + if ((cnt.v_free_count > vm_pageout_free_min) /* XXX */ + && pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { + vm_page_activate(m); + } else if (!m->act_count) { + pmap_page_protect(VM_PAGE_TO_PHYS(m), + VM_PROT_NONE); + vm_page_free(m); + ++pages_freed; + } else { + m->act_count -= min(m->act_count, ACT_DECLINE); + TAILQ_REMOVE(&vm_page_queue_inactive, m, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_inactive, m, pageq); + } + } else if ((m->flags & PG_LAUNDRY) && maxlaunder > 0) { + int written; + if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { + pmap_clear_reference(VM_PAGE_TO_PHYS(m)); + vm_page_activate(m); + m = next; + continue; + } + /* + * If a page is dirty, then it is either + * being washed (but not yet cleaned) + * or it is still in the laundry. If it is + * still in the laundry, then we start the + * cleaning operation. + */ - pmap_clear_reference(VM_PAGE_TO_PHYS(m)); + if (written = vm_pageout_clean(m,0)) { + maxlaunder -= written; + } + /* + * if the next page has been re-activated, start scanning again + */ + if (next && (next->flags & PG_INACTIVE) == 0) + goto rescan1; + } else if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { + pmap_clear_reference(VM_PAGE_TO_PHYS(m)); + vm_page_activate(m); + } + m = next; + } /* - * 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. + * now check malloc area or swap processes out if we are in low + * memory conditions */ - if (pageout_status != VM_PAGER_PEND) { - m->flags &= ~PG_BUSY; - PAGE_WAKEUP(m); - object->paging_in_progress--; + if (cnt.v_free_count <= cnt.v_free_min) { + /* + * swap out inactive processes + */ + swapout_threads(); } -} - -#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. + * 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. */ - vm_pager_cluster(object->pager, m->offset, &loff, &hoff); - if (hoff - loff == PAGE_SIZE) { - vm_pageout_page(m, object); - return; + + page_shortage = cnt.v_inactive_target - + (cnt.v_free_count + cnt.v_inactive_count); + + if (page_shortage <= 0) { + if (pages_freed == 0) { + if( cnt.v_free_count < cnt.v_free_min) { + page_shortage = cnt.v_free_min - cnt.v_free_count; + } else if(((cnt.v_free_count + cnt.v_inactive_count) < + (cnt.v_free_min + cnt.v_inactive_target))) { + page_shortage = 1; + } else { + page_shortage = 0; + } + } + } - plistp = plist; + maxscan = cnt.v_active_count; + m = vm_page_queue_active.tqh_first; + while (m && maxscan-- && (page_shortage > 0)) { - /* - * 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; + next = m->pageq.tqe_next; - /* - * 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++; + /* + * Don't deactivate pages that are busy. + */ + if ((m->flags & PG_BUSY) || (m->hold_count != 0)) { + m = next; + continue; + } + + if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { + pmap_clear_reference(VM_PAGE_TO_PHYS(m)); + if (m->act_count < ACT_MAX) + m->act_count += ACT_ADVANCE; + TAILQ_REMOVE(&vm_page_queue_active, m, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq); + TAILQ_REMOVE(&m->object->memq, m, listq); + TAILQ_INSERT_TAIL(&m->object->memq, m, listq); + } else { + m->act_count -= min(m->act_count, ACT_DECLINE); + + /* + * if the page act_count is zero -- then we deactivate + */ + if (!m->act_count) { + vm_page_deactivate(m); + --page_shortage; + /* + * else if on the next go-around we will deactivate the page + * we need to place the page on the end of the queue to age + * the other pages in memory. + */ + } else { + TAILQ_REMOVE(&vm_page_queue_active, m, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq); + TAILQ_REMOVE(&m->object->memq, m, listq); + TAILQ_INSERT_TAIL(&m->object->memq, m, listq); + } + } + + m = next; } - plistp += atop(offset - loff); - loff = offset; /* - * Now do the same moving forward from the target. + * if we have not freed any pages and we are desparate for memory + * then we keep trying until we get some (any) memory. */ - 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++; + + if( !force_wakeup && (swap_pager_full || !force_wakeup || + (pages_freed == 0 && (cnt.v_free_count < cnt.v_free_min)))){ + vm_pager_sync(); + force_wakeup = 1; + goto morefree; } - hoff = offset; + vm_page_pagesfreed += pages_freed; + return force_wakeup; +} - /* - * 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((int) &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 +vm_pagescan() +{ + int maxscan, pages_scanned, pages_referenced, nextscan, scantick = hz/20; + int m_ref, next_ref; + vm_page_t m, next; - (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(); + (void) spl0(); + + nextscan = scantick; + +scanloop: + + pages_scanned = 0; + pages_referenced = 0; + maxscan = min(cnt.v_active_count, MAXSCAN); /* - * Loop through the affected pages, reflecting the outcome of - * the operation. + * Gather statistics on page usage. */ - 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)); + m = vm_page_queue_active.tqh_first; + while (m && (maxscan-- > 0)) { + + ++pages_scanned; + + next = m->pageq.tqe_next; + /* - * If the operation is still going, leave the page busy - * to block all other accesses. + * Dont mess with pages that are busy. */ - if (postatus != VM_PAGER_PEND) { - p->flags &= ~PG_BUSY; - PAGE_WAKEUP(p); + if ((m->flags & PG_BUSY) || (m->hold_count != 0)) { + TAILQ_REMOVE(&vm_page_queue_active, m, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq); + m = next; + continue; + } + /* + * Advance pages that have been referenced, decline pages that + * have not. + */ + if (pmap_is_referenced(VM_PAGE_TO_PHYS(m))) { + pmap_clear_reference(VM_PAGE_TO_PHYS(m)); + pages_referenced++; + if (m->act_count < ACT_MAX) + m->act_count += ACT_ADVANCE; + TAILQ_REMOVE(&vm_page_queue_active, m, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq); + TAILQ_REMOVE(&m->object->memq, m, listq); + TAILQ_INSERT_TAIL(&m->object->memq, m, listq); + } else { + m->act_count -= min(m->act_count, ACT_DECLINE); + /* + * if the page act_count is zero, and we are low on mem -- then we deactivate + */ + if (!m->act_count && + (cnt.v_free_count+cnt.v_inactive_count < cnt.v_free_target+cnt.v_inactive_target )) { + vm_page_deactivate(m); + /* + * else if on the next go-around we will deactivate the page + * we need to place the page on the end of the queue to age + * the other pages in memory. + */ + } else { + TAILQ_REMOVE(&vm_page_queue_active, m, pageq); + TAILQ_INSERT_TAIL(&vm_page_queue_active, m, pageq); + TAILQ_REMOVE(&m->object->memq, m, listq); + TAILQ_INSERT_TAIL(&m->object->memq, m, listq); + } } + m = next; } - /* - * 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--; + if (pages_referenced) { + nextscan = (pages_scanned / pages_referenced) * scantick; + nextscan = max(nextscan, scantick); + nextscan = min(nextscan, hz); + } else + nextscan = hz; + tsleep((caddr_t) &vm_pagescanner, PVM, "scanw", nextscan); + + goto scanloop; } -#endif /* * vm_pageout is the high level pageout daemon. */ - -void vm_pageout() +void +vm_pageout() { + extern npendingio, swiopend; + static nowakeup; (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 */ +vmretry: + cnt.v_free_min = 12; + cnt.v_free_reserved = 8; + if (cnt.v_free_min < 8) + cnt.v_free_min = 8; + if (cnt.v_free_min > 32) + cnt.v_free_min = 32; + vm_pageout_free_min = 4; + cnt.v_free_target = 2*cnt.v_free_min + cnt.v_free_reserved; + cnt.v_inactive_target = cnt.v_free_count / 12; + cnt.v_free_min += cnt.v_free_reserved; + + /* XXX does not really belong here */ if (vm_page_max_wired == 0) vm_page_max_wired = cnt.v_free_count / 3; + + (void) swap_pager_alloc(0, 0, 0, 0); + /* * The pageout daemon is never done, so loop * forever. */ - - simple_lock(&vm_pages_needed_lock); while (TRUE) { - thread_sleep((int) &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; - + int force_wakeup; + extern struct loadavg averunnable; +/* + cnt.v_free_min = 12 + averunnable.ldavg[0] / 1024; + cnt.v_free_target = 2*cnt.v_free_min + cnt.v_free_reserved; + cnt.v_inactive_target = cnt.v_free_target*2; +*/ + + tsleep((caddr_t) &vm_pages_needed, PVM, "psleep", 0); + + vm_pager_sync(); /* - * 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. + * The force wakeup hack added to eliminate delays and potiential + * deadlock. It was possible for the page daemon to indefintely + * postpone waking up a process that it might be waiting for memory + * on. The putmulti stuff seems to have aggravated the situation. */ - if (cnt.v_free_count < cnt.v_free_target || - cnt.v_inactive_count < cnt.v_inactive_target) - vm_pageout_scan(); + force_wakeup = vm_pageout_scan(); vm_pager_sync(); - simple_lock(&vm_pages_needed_lock); - thread_wakeup((int) &cnt.v_free_count); + if( force_wakeup) + wakeup( (caddr_t) &cnt.v_free_count); + cnt.v_scan++; + wakeup((caddr_t) kmem_map); } } + |