* 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.
* %sccs.include.redist.c%
* @(#)vm_pageout.c 8.6 (Berkeley) %G%
* Copyright (c) 1987, 1990 Carnegie-Mellon University.
* 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 <vm/vm_pageout.h>
#define VM_PAGE_FREE_MIN (cnt.v_free_count / 20)
#ifndef VM_PAGE_FREE_TARGET
#define VM_PAGE_FREE_TARGET ((cnt.v_free_min * 4) / 3)
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 */
#define MAXPOCLUSTER (MAXPHYS/NBPG) /* XXX */
int doclustered_pageout
= 1;
* vm_pageout_scan does the dirty work for the pageout daemon.
register vm_page_t m
, next
;
register int page_shortage
;
register int pages_freed
;
* Only continue when we want more pages to be "free"
simple_lock(&vm_page_queue_free_lock
);
simple_unlock(&vm_page_queue_free_lock
);
if (free
< cnt
.v_free_target
) {
* Be sure the pmap system is updated so
* we can scan the inactive queue.
* Acquire the resident page system lock,
* as we may be changing what's resident quite a bit.
* 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.
for (m
= vm_page_queue_inactive
.tqh_first
; m
!= NULL
; m
= next
) {
simple_lock(&vm_page_queue_free_lock
);
simple_unlock(&vm_page_queue_free_lock
);
if (free
>= cnt
.v_free_target
)
next
= m
->pageq
.tqe_next
;
* If the page has been referenced, move it back to the
if (pmap_is_referenced(VM_PAGE_TO_PHYS(m
))) {
* If the page is clean, free it up.
if (m
->flags
& PG_CLEAN
) {
if (vm_object_lock_try(object
)) {
pmap_page_protect(VM_PAGE_TO_PHYS(m
),
vm_object_unlock(object
);
* If the page is dirty but already being washed, skip it.
if ((m
->flags
& PG_LAUNDRY
) == 0)
* Otherwise the page is dirty and still in the laundry,
* so we start the cleaning operation and remove it from
if (!vm_object_lock_try(object
))
vm_pager_cancluster(object
->pager
, PG_CLUSTERPUT
))
vm_pageout_cluster(m
, object
);
vm_pageout_page(m
, 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
page_shortage
= cnt
.v_inactive_target
- cnt
.v_inactive_count
;
if (page_shortage
<= 0 && pages_freed
== 0)
while (page_shortage
> 0) {
* Move some more pages from active to inactive.
if ((m
= vm_page_queue_active
.tqh_first
) == NULL
)
* 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.
vm_pageout_page(m
, object
)
* We set the busy bit to cause potential page faults on
* 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
);
* Try to collapse the object before making a pager for it.
* We must unlock the page queues first.
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
if ((pager
= object
->pager
) == NULL
) {
pager
= vm_pager_allocate(PG_DFLT
, (caddr_t
)0, object
->size
,
VM_PROT_ALL
, (vm_offset_t
)0);
vm_object_setpager(object
, pager
, 0, FALSE
);
pageout_status
= pager
? vm_pager_put(pager
, m
, FALSE
) : VM_PAGER_FAIL
;
switch (pageout_status
) {
* Page outside of range of object. Right now we
* essentially lose the changes by pretending it
* XXX dubious, what should we do?
pmap_clear_modify(VM_PAGE_TO_PHYS(m
));
* 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);
* 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).
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
if (pageout_status
!= VM_PAGER_PEND
) {
object
->paging_in_progress
--;
((((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
vm_pageout_cluster(m
, object
)
vm_offset_t offset
, loff
, hoff
;
vm_page_t plist
[MAXPOCLUSTER
], *plistp
, p
;
* Determine the range of pages that can be part of a cluster
* for this object/offset. If it is only our single page, just
vm_pager_cluster(object
->pager
, m
->offset
, &loff
, &hoff
);
if (hoff
- loff
== PAGE_SIZE
) {
vm_pageout_page(m
, object
);
* Target page is always part of the cluster.
pmap_page_protect(VM_PAGE_TO_PHYS(m
), VM_PROT_NONE
);
plistp
[atop(m
->offset
- loff
)] = m
;
* 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
);
while (offset
> loff
&& count
< MAXPOCLUSTER
-1) {
p
= vm_page_lookup(object
, offset
- PAGE_SIZE
);
if (p
== NULL
|| !PAGEOUTABLE(p
))
pmap_page_protect(VM_PAGE_TO_PHYS(p
), VM_PROT_NONE
);
plistp
+= atop(offset
- loff
);
* 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
))
pmap_page_protect(VM_PAGE_TO_PHYS(p
), VM_PROT_NONE
);
* Unlock everything and do a wakeup prior to the pager call
object
->paging_in_progress
++;
vm_object_unlock(object
);
thread_wakeup(&cnt
.v_free_count
);
postatus
= vm_pager_put_pages(object
->pager
, plistp
, count
, FALSE
);
if (postatus
== VM_PAGER_AGAIN
) {
(void) tsleep((caddr_t
)&lbolt
, PZERO
|PCATCH
, "pageout", 0);
} else if (postatus
== VM_PAGER_BAD
)
panic("vm_pageout_cluster: VM_PAGER_BAD");
* Loop through the affected pages, reflecting the outcome of
for (ix
= 0; ix
< count
; ix
++) {
* Pageout failed, reactivate the target page so it
* doesn't clog the inactive list. Other pages are
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
) {
* 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
--;
* vm_pageout is the high level pageout daemon.
* 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
simple_lock(&vm_pages_needed_lock
);
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_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
)
simple_lock(&vm_pages_needed_lock
);
thread_wakeup(&cnt
.v_free_count
);