* Copyright (c) 1991 Regents of the University of California.
* 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
* 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
* from: @(#)vm_object.c 7.4 (Berkeley) 5/7/91
* 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.
* Virtual memory object module.
* Virtual memory objects maintain the actual data
* associated with allocated virtual memory. A given
* page of memory exists within exactly one object.
* An object is only deallocated when all "references"
* are given up. Only one "reference" to a given
* region of an object should be writeable.
* Associated with each object is a list of all resident
* memory pages belonging to that object; this list is
* maintained by the "vm_page" module, and locked by the object's
* Each object also records a "pager" routine which is
* used to retrieve (and store) pages to the proper backing
* storage. In addition, objects may be backed by other
* objects from which they were virtual-copied.
* The only items within the object structure which are
* modified after time of creation are:
* reference count locked by object's lock
* pager routine locked by object's lock
struct vm_object kernel_object_store
;
struct vm_object kmem_object_store
;
#define VM_OBJECT_HASH_COUNT 157
int vm_cache_max
= 100; /* can patch if necessary */
queue_head_t vm_object_hashtable
[VM_OBJECT_HASH_COUNT
];
long object_collapses
= 0;
long object_bypasses
= 0;
* Initialize the VM objects module.
queue_init(&vm_object_cached_list
);
queue_init(&vm_object_list
);
simple_lock_init(&vm_cache_lock
);
simple_lock_init(&vm_object_list_lock
);
for (i
= 0; i
< VM_OBJECT_HASH_COUNT
; i
++)
queue_init(&vm_object_hashtable
[i
]);
kernel_object
= &kernel_object_store
;
_vm_object_allocate(VM_MAX_KERNEL_ADDRESS
- VM_MIN_KERNEL_ADDRESS
,
kmem_object
= &kmem_object_store
;
_vm_object_allocate(VM_KMEM_SIZE
+ VM_MBUF_SIZE
, kmem_object
);
* Returns a new object with the given size.
vm_object_t
vm_object_allocate(size
)
register vm_object_t result
;
malloc((u_long
)sizeof *result
, M_VMOBJ
, M_WAITOK
);
_vm_object_allocate(size
, result
);
_vm_object_allocate(size
, object
)
register vm_object_t object
;
queue_init(&object
->memq
);
vm_object_lock_init(object
);
object
->resident_page_count
= 0;
object
->can_persist
= FALSE
;
object
->paging_in_progress
= 0;
* Object starts out read-write, with no pager.
object
->pager_ready
= FALSE
;
object
->internal
= TRUE
; /* vm_allocate_with_pager will reset */
object
->paging_offset
= 0;
object
->shadow_offset
= (vm_offset_t
) 0;
simple_lock(&vm_object_list_lock
);
queue_enter(&vm_object_list
, object
, vm_object_t
, object_list
);
simple_unlock(&vm_object_list_lock
);
* Gets another reference to the given object.
void vm_object_reference(object
)
register vm_object_t object
;
vm_object_unlock(object
);
* Release a reference to the specified object,
* gained either through a vm_object_allocate
* or a vm_object_reference call. When all references
* are gone, storage associated with this object
* No object may be locked.
void vm_object_deallocate(object
)
register vm_object_t object
;
* The cache holds a reference (uncounted) to
* the object; we must lock it before removing
if (--(object
->ref_count
) != 0) {
* If there are still references, then
vm_object_unlock(object
);
vm_object_cache_unlock();
* See if this object can persist. If so, enter
* it in the cache, then deactivate all of its
if (object
->can_persist
) {
* Check for dirty pages in object
* Print warning as this may signify kernel bugs
* pk@cs.few.eur.nl - 4/15/93
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
if (pmap_is_modified(VM_PAGE_TO_PHYS(p
)) ||
printf("vm_object_dealloc: persistent object %x isn't clean\n", object
);
p
= (vm_page_t
) queue_next(&p
->listq
);
queue_enter(&vm_object_cached_list
, object
,
vm_object_t
, cached_list
);
vm_object_cache_unlock();
vm_object_deactivate_pages(object
);
vm_object_unlock(object
);
* Make sure no one can look us up now.
vm_object_remove(object
->pager
);
vm_object_cache_unlock();
vm_object_terminate(object
);
/* unlocks and deallocates object */
* vm_object_terminate actually destroys the specified object, freeing
* up all previously used resources.
* The object must be locked.
void vm_object_terminate(object
)
register vm_object_t object
;
vm_object_t shadow_object
;
* Detach the object from its shadow if we are the shadow's
if ((shadow_object
= object
->shadow
) != NULL
) {
vm_object_lock(shadow_object
);
if (shadow_object
->copy
== object
)
shadow_object
->copy
= NULL
;
else if (shadow_object
->copy
!= NULL
)
panic("vm_object_terminate: copy/shadow inconsistency");
vm_object_unlock(shadow_object
);
* Wait until the pageout daemon is through
while (object
->paging_in_progress
!= 0) {
vm_object_sleep(object
, object
, FALSE
);
* While the paging system is locked,
* pull the object's pages off the active
* and inactive queues. This keeps the
* pageout daemon from playing with them
* during vm_pager_deallocate.
* We can't free the pages yet, because the
* object's pager may have to write them out
* before deallocating the paging space.
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
queue_remove(&vm_page_queue_active
, p
, vm_page_t
,
queue_remove(&vm_page_queue_inactive
, p
, vm_page_t
,
vm_page_inactive_count
--;
p
= (vm_page_t
) queue_next(&p
->listq
);
vm_object_unlock(object
);
if (object
->paging_in_progress
!= 0)
panic("vm_object_deallocate: pageout in progress");
* Clean and free the pages, as appropriate.
* All references to the object are gone,
* so we don't need to lock it.
vm_object_page_clean(object
, 0, 0);
vm_object_unlock(object
);
while (!queue_empty(&object
->memq
)) {
p
= (vm_page_t
) queue_first(&object
->memq
);
* Let the pager know object is dead.
if (object
->pager
!= NULL
)
vm_pager_deallocate(object
->pager
);
simple_lock(&vm_object_list_lock
);
queue_remove(&vm_object_list
, object
, vm_object_t
, object_list
);
simple_unlock(&vm_object_list_lock
);
* Free the space for the object.
free((caddr_t
)object
, M_VMOBJ
);
* Clean all dirty pages in the specified range of object.
* Leaves page on whatever queue it is currently on.
* Odd semantics: if start == end, we clean everything.
* The object must be locked.
vm_object_page_clean(object
, start
, end
)
register vm_object_t object
;
register vm_offset_t start
;
register vm_offset_t end
;
if (object
->pager
== NULL
)
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
p
->offset
>= start
&& p
->offset
< end
) {
if (p
->clean
&& pmap_is_modified(VM_PAGE_TO_PHYS(p
)))
pmap_page_protect(VM_PAGE_TO_PHYS(p
), VM_PROT_NONE
);
object
->paging_in_progress
++;
vm_object_unlock(object
);
(void) vm_pager_put(object
->pager
, p
, TRUE
);
object
->paging_in_progress
--;
p
= (vm_page_t
) queue_next(&p
->listq
);
* vm_object_deactivate_pages
* Deactivate all pages in the specified object. (Keep its pages
* in memory even though it is no longer referenced.)
* The object must be locked.
vm_object_deactivate_pages(object
)
register vm_object_t object
;
register vm_page_t p
, next
;
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
next
= (vm_page_t
) queue_next(&p
->listq
);
vm_page_deactivate(p
); /* optimisation from mach 3.0 -
* andrew@werple.apana.org.au,
* Trim the object cache to size.
register vm_object_t object
;
while (vm_object_cached
> vm_cache_max
) {
object
= (vm_object_t
) queue_first(&vm_object_cached_list
);
vm_object_cache_unlock();
if (object
!= vm_object_lookup(object
->pager
))
panic("vm_object_deactivate: I'm sooo confused.");
pager_cache(object
, FALSE
);
vm_object_cache_unlock();
* Shut down the object system. Unfortunately, while we
* may be trying to do this, init is happily waiting for
* processes to exit, and therefore will be causing some objects
* to be deallocated. To handle this, we gain a fake reference
* to all objects we release paging areas for. This will prevent
* a duplicate deallocation. This routine is probably full of
void vm_object_shutdown()
register vm_object_t object
;
* Clean up the object cache *before* we screw up the reference
* counts on all of the objects.
printf("free paging spaces: ");
* First we gain a reference to each object so that
* no one else will deallocate them.
simple_lock(&vm_object_list_lock
);
object
= (vm_object_t
) queue_first(&vm_object_list
);
while (!queue_end(&vm_object_list
, (queue_entry_t
) object
)) {
vm_object_reference(object
);
object
= (vm_object_t
) queue_next(&object
->object_list
);
simple_unlock(&vm_object_list_lock
);
* Now we deallocate all the paging areas. We don't need
* to lock anything because we've reduced to a single
* processor while shutting down. This also assumes that
* no new objects are being created.
object
= (vm_object_t
) queue_first(&vm_object_list
);
while (!queue_end(&vm_object_list
, (queue_entry_t
) object
)) {
if (object
->pager
!= NULL
)
vm_pager_deallocate(object
->pager
);
object
= (vm_object_t
) queue_next(&object
->object_list
);
* Makes all physical pages in the specified
* object range copy-on-write. No writeable
* references to these pages should remain.
* The object must *not* be locked.
void vm_object_pmap_copy(object
, start
, end
)
register vm_object_t object
;
register vm_offset_t start
;
register vm_offset_t end
;
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
if ((start
<= p
->offset
) && (p
->offset
< end
)) {
pmap_page_protect(VM_PAGE_TO_PHYS(p
), VM_PROT_READ
);
p
= (vm_page_t
) queue_next(&p
->listq
);
vm_object_unlock(object
);
* Removes all physical pages in the specified
* object range from all physical maps.
* The object must *not* be locked.
void vm_object_pmap_remove(object
, start
, end
)
register vm_object_t object
;
register vm_offset_t start
;
register vm_offset_t end
;
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
if ((start
<= p
->offset
) && (p
->offset
< end
))
pmap_page_protect(VM_PAGE_TO_PHYS(p
), VM_PROT_NONE
);
p
= (vm_page_t
) queue_next(&p
->listq
);
vm_object_unlock(object
);
* Create a new object which is a copy of an existing
* object, and mark all of the pages in the existing
* object 'copy-on-write'. The new object has one reference.
* Returns the new object.
* May defer the copy until later if the object is not backed
* up by a non-default pager.
void vm_object_copy(src_object
, src_offset
, size
,
dst_object
, dst_offset
, src_needs_copy
)
register vm_object_t src_object
;
vm_object_t
*dst_object
; /* OUT */
vm_offset_t
*dst_offset
; /* OUT */
boolean_t
*src_needs_copy
; /* OUT */
register vm_object_t new_copy
;
register vm_object_t old_copy
;
vm_offset_t new_start
, new_end
;
if (src_object
== NULL
) {
* If the object's pager is null_pager or the
* default pager, we don't have to make a copy
* of it. Instead, we set the needs copy flag and
vm_object_lock(src_object
);
if (src_object
->pager
== NULL
||
* Make another reference to the object
* Mark all of the pages copy-on-write.
for (p
= (vm_page_t
) queue_first(&src_object
->memq
);
!queue_end(&src_object
->memq
, (queue_entry_t
)p
);
p
= (vm_page_t
) queue_next(&p
->listq
)) {
if (src_offset
<= p
->offset
&&
p
->offset
< src_offset
+ size
)
vm_object_unlock(src_object
);
*dst_object
= src_object
;
*dst_offset
= src_offset
;
* Must make a shadow when write is desired
* Try to collapse the object before copying it.
vm_object_collapse(src_object
);
* If the object has a pager, the pager wants to
* see all of the changes. We need a copy-object
* If there is a copy-object, and it is empty,
* no changes have been made to the object since the
* copy-object was made. We can use the same copy-
old_copy
= src_object
->copy
;
* Try to get the locks (out of order)
if (!vm_object_lock_try(old_copy
)) {
vm_object_unlock(src_object
);
/* should spin a bit here... */
vm_object_lock(src_object
);
if (old_copy
->resident_page_count
== 0 &&
old_copy
->pager
== NULL
) {
* Return another reference to
* the existing copy-object.
vm_object_unlock(old_copy
);
vm_object_unlock(src_object
);
*dst_offset
= src_offset
;
vm_object_unlock(old_copy
);
vm_object_unlock(src_object
);
* If the object has a pager, the pager wants
* to see all of the changes. We must make
* a copy-object and put the changed pages there.
* The copy-object is always made large enough to
* completely shadow the original object, since
* it may have several users who want to shadow
* the original object at different points.
new_copy
= vm_object_allocate(src_object
->size
);
vm_object_lock(src_object
);
* Copy object may have changed while we were unlocked
old_copy
= src_object
->copy
;
* Try to get the locks (out of order)
if (!vm_object_lock_try(old_copy
)) {
vm_object_unlock(src_object
);
if (old_copy
->shadow
!= src_object
||
old_copy
->shadow_offset
!= (vm_offset_t
) 0)
panic("vm_object_copy: copy/shadow inconsistency");
* Make the old copy-object shadow the new one.
* It will receive no more pages from the original
src_object
->ref_count
--; /* remove ref. from old_copy */
old_copy
->shadow
= new_copy
;
new_copy
->ref_count
++; /* locking not needed - we
vm_object_unlock(old_copy
); /* done with old_copy */
new_start
= (vm_offset_t
) 0; /* always shadow original at 0 */
new_end
= (vm_offset_t
) new_copy
->size
; /* for the whole object */
* Point the new copy at the existing object.
new_copy
->shadow
= src_object
;
new_copy
->shadow_offset
= new_start
;
src_object
->copy
= new_copy
;
* Mark all the affected pages of the existing object
p
= (vm_page_t
) queue_first(&src_object
->memq
);
while (!queue_end(&src_object
->memq
, (queue_entry_t
) p
)) {
if ((new_start
<= p
->offset
) && (p
->offset
< new_end
))
p
= (vm_page_t
) queue_next(&p
->listq
);
vm_object_unlock(src_object
);
*dst_offset
= src_offset
- new_start
;
* Create a new object which is backed by the
* specified existing object range. The source
* object reference is deallocated.
* The new object and offset into that object
* are returned in the source parameters.
void vm_object_shadow(object
, offset
, length
)
vm_object_t
*object
; /* IN/OUT */
vm_offset_t
*offset
; /* IN/OUT */
register vm_object_t source
;
register vm_object_t result
;
* Allocate a new object with the given length
if ((result
= vm_object_allocate(length
)) == NULL
)
panic("vm_object_shadow: no object for shadowing");
* The new object shadows the source object, adding
* a reference to it. Our caller changes his reference
* to point to the new object, removing a reference to
* the source object. Net result: no change of reference
* Store the offset into the source object,
* and fix up the offset into the new object.
result
->shadow_offset
= *offset
;
* Set the specified object's pager to the specified pager.
void vm_object_setpager(object
, pager
, paging_offset
,
vm_offset_t paging_offset
;
read_only
++; /* No longer used */
vm_object_lock(object
); /* XXX ? */
object
->paging_offset
= paging_offset
;
vm_object_unlock(object
); /* XXX ? */
* vm_object_hash hashes the pager/id pair.
#define vm_object_hash(pager) \
(((unsigned)pager)%VM_OBJECT_HASH_COUNT)
* vm_object_lookup looks in the object cache for an object with the
* specified pager and paging id.
vm_object_t
vm_object_lookup(pager
)
register vm_object_hash_entry_t entry
;
bucket
= &vm_object_hashtable
[vm_object_hash(pager
)];
entry
= (vm_object_hash_entry_t
) queue_first(bucket
);
while (!queue_end(bucket
, (queue_entry_t
) entry
)) {
if (object
->pager
== pager
) {
if (object
->ref_count
== 0) {
queue_remove(&vm_object_cached_list
, object
,
vm_object_t
, cached_list
);
vm_object_unlock(object
);
vm_object_cache_unlock();
entry
= (vm_object_hash_entry_t
) queue_next(&entry
->hash_links
);
vm_object_cache_unlock();
* vm_object_enter enters the specified object/pager/id into
void vm_object_enter(object
, pager
)
register vm_object_hash_entry_t entry
;
* We don't cache null objects, and we can't cache
* objects with the null pager.
bucket
= &vm_object_hashtable
[vm_object_hash(pager
)];
entry
= (vm_object_hash_entry_t
)
malloc((u_long
)sizeof *entry
, M_VMOBJHASH
, M_WAITOK
);
object
->can_persist
= TRUE
;
queue_enter(bucket
, entry
, vm_object_hash_entry_t
, hash_links
);
vm_object_cache_unlock();
* Remove the pager from the hash table.
* Note: This assumes that the object cache
* is locked. XXX this should be fixed
* by reorganizing vm_object_deallocate.
register vm_pager_t pager
;
register vm_object_hash_entry_t entry
;
register vm_object_t object
;
bucket
= &vm_object_hashtable
[vm_object_hash(pager
)];
entry
= (vm_object_hash_entry_t
) queue_first(bucket
);
while (!queue_end(bucket
, (queue_entry_t
) entry
)) {
if (object
->pager
== pager
) {
queue_remove(bucket
, entry
, vm_object_hash_entry_t
,
free((caddr_t
)entry
, M_VMOBJHASH
);
entry
= (vm_object_hash_entry_t
) queue_next(&entry
->hash_links
);
* vm_object_cache_clear removes all objects from the cache.
void vm_object_cache_clear()
register vm_object_t object
;
* Remove each object in the cache by scanning down the
* list of cached objects.
while (!queue_empty(&vm_object_cached_list
)) {
object
= (vm_object_t
) queue_first(&vm_object_cached_list
);
vm_object_cache_unlock();
* Note: it is important that we use vm_object_lookup
* to gain a reference, and not vm_object_reference, because
* the logic for removing an object from the cache lies in
if (object
!= vm_object_lookup(object
->pager
))
panic("vm_object_cache_clear: I'm sooo confused.");
pager_cache(object
, FALSE
);
vm_object_cache_unlock();
boolean_t vm_object_collapse_allowed
= TRUE
;
* Collapse an object with the object backing it.
* Pages in the backing object are moved into the
* parent, and the backing object is deallocated.
* Requires that the object be locked and the page
void vm_object_collapse(object
)
register vm_object_t object
;
register vm_object_t backing_object
;
register vm_offset_t backing_offset
;
register vm_offset_t new_offset
;
register vm_page_t p
, pp
;
if (!vm_object_collapse_allowed
)
* Verify that the conditions are right for collapse:
* The object exists and no pages in it are currently
* being paged out (or have ever been paged out).
object
->paging_in_progress
!= 0 ||
* There is a backing object, and
if ((backing_object
= object
->shadow
) == NULL
)
vm_object_lock(backing_object
);
* The backing object is not read_only,
* and no pages in the backing object are
* currently being paged out.
* The backing object is internal.
if (!backing_object
->internal
||
backing_object
->paging_in_progress
!= 0) {
vm_object_unlock(backing_object
);
* The backing object can't be a copy-object:
* the shadow_offset for the copy-object must stay
* as 0. Furthermore (for the 'we have all the
* pages' case), if we bypass backing_object and
* just shadow the next object in the chain, old
* pages from that object would then have to be copied
* BOTH into the (former) backing_object and into the
if (backing_object
->shadow
!= NULL
&&
backing_object
->shadow
->copy
!= NULL
) {
vm_object_unlock(backing_object
);
* We know that we can either collapse the backing
* object (if the parent is the only reference to
* it) or (perhaps) remove the parent's reference
backing_offset
= object
->shadow_offset
;
* If there is exactly one reference to the backing
* object, we can collapse it into the parent.
if (backing_object
->ref_count
== 1) {
* We can collapse the backing object.
* Move all in-memory pages from backing_object
* to the parent. Pages that have been paged out
* will be overwritten by any of the parent's
* pages that shadow them.
while (!queue_empty(&backing_object
->memq
)) {
queue_first(&backing_object
->memq
);
new_offset
= (p
->offset
- backing_offset
);
* If the parent has a page here, or if
* this page falls outside the parent,
* Otherwise, move it as planned.
if (p
->offset
< backing_offset
||
pp
= vm_page_lookup(object
, new_offset
);
if (pp
!= NULL
&& !pp
->fake
) {
* This should never happen -- the
* parent cannot have ever had an
* external memory object, and thus
* cannot have absent pages.
panic("vm_object_collapse: bad case");
/* andrew@werple.apana.org.au - from
/* may be someone waiting for it */
* Parent now has no page.
* Move the backing object's page
vm_page_rename(p
, object
, new_offset
);
* Move the pager from backing_object to object.
* XXX We're only using part of the paging space
* for keeps now... we ought to discard the
object
->pager
= backing_object
->pager
;
/* andrew@werple.apana.org.au, 12 Feb 1993 */
* If there is no pager, leave paging-offset alone.
backing_object
->paging_offset
+
object
->paging_offset
+= backing_offset
;
backing_object
->pager
= NULL
;
* Object now shadows whatever backing_object did.
* Note that the reference to backing_object->shadow
* moves from within backing_object to within object.
object
->shadow
= backing_object
->shadow
;
object
->shadow_offset
+= backing_object
->shadow_offset
;
if (object
->shadow
!= NULL
&&
object
->shadow
->copy
!= NULL
) {
panic("vm_object_collapse: we collapsed a copy-object!");
* Discard backing_object.
* Since the backing object has no pages, no
* pager left, and no object references within it,
* all that is necessary is to dispose of it.
vm_object_unlock(backing_object
);
simple_lock(&vm_object_list_lock
);
queue_remove(&vm_object_list
, backing_object
,
vm_object_t
, object_list
);
simple_unlock(&vm_object_list_lock
);
free((caddr_t
)backing_object
, M_VMOBJ
);
* If all of the pages in the backing object are
* shadowed by the parent object, the parent
* object no longer has to shadow the backing
* object; it can shadow the next one in the
* The backing object must not be paged out - we'd
* have to check all of the paged-out pages, as
if (backing_object
->pager
!= NULL
) {
vm_object_unlock(backing_object
);
* Should have a check for a 'small' number
p
= (vm_page_t
) queue_first(&backing_object
->memq
);
while (!queue_end(&backing_object
->memq
,
new_offset
= (p
->offset
- backing_offset
);
* If the parent has a page here, or if
* this page falls outside the parent,
* Otherwise, the backing_object must be
if (p
->offset
>= backing_offset
&&
((pp
= vm_page_lookup(object
, new_offset
))
vm_object_unlock(backing_object
);
p
= (vm_page_t
) queue_next(&p
->listq
);
* Make the parent shadow the next object
* in the chain. Deallocating backing_object
* will not remove it, since its reference
vm_object_reference(object
->shadow
= backing_object
->shadow
);
object
->shadow_offset
+= backing_object
->shadow_offset
;
/* andrew@werple.apana.org.au, 12 Feb 1993 */
* Backing object might have had a copy pointer
* to us. If it did, clear it.
if (backing_object
->copy
== object
)
backing_object
->copy
= NULL
;
/* Drop the reference count on backing_object.
* Since its ref_count was at least 2, it
* will not vanish; so we don't need to call
backing_object
->ref_count
--;
vm_object_unlock(backing_object
);
* Try again with this object's new backing object.
* vm_object_page_remove: [internal]
* Removes all physical pages in the specified
* object range from the object's list of pages.
* The object must be locked.
void vm_object_page_remove(object
, start
, end
)
register vm_object_t object
;
register vm_offset_t start
;
register vm_offset_t end
;
register vm_page_t p
, next
;
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
next
= (vm_page_t
) queue_next(&p
->listq
);
if ((start
<= p
->offset
) && (p
->offset
< end
)) {
pmap_page_protect(VM_PAGE_TO_PHYS(p
), VM_PROT_NONE
);
* Routine: vm_object_coalesce
* Function: Coalesces two objects backing up adjoining
* regions of memory into a single object.
* returns TRUE if objects were combined.
* NOTE: Only works at the moment if the second object is NULL -
* if it's not, which object do we lock first?
* prev_object First object to coalesce
* prev_offset Offset into prev_object
* next_object Second object into coalesce
* next_offset Offset into next_object
* prev_size Size of reference to prev_object
* next_size Size of reference to next_object
* The object must *not* be locked.
boolean_t
vm_object_coalesce(prev_object
, next_object
,
prev_offset
, next_offset
,
register vm_object_t prev_object
;
vm_offset_t prev_offset
, next_offset
;
vm_size_t prev_size
, next_size
;
if (next_object
!= NULL
) {
if (prev_object
== NULL
) {
vm_object_lock(prev_object
);
* Try to collapse the object first
vm_object_collapse(prev_object
);
* . more than one reference
* . shadows another object
* (any of which mean that the pages not mapped to
* prev_entry may be in use anyway)
if (prev_object
->ref_count
> 1 ||
prev_object
->pager
!= NULL
||
prev_object
->shadow
!= NULL
||
prev_object
->copy
!= NULL
) {
vm_object_unlock(prev_object
);
* Remove any pages that may still be in the object from
* a previous deallocation.
vm_object_page_remove(prev_object
,
prev_offset
+ prev_size
+ next_size
);
* Extend the object if necessary.
newsize
= prev_offset
+ prev_size
+ next_size
;
if (newsize
> prev_object
->size
)
prev_object
->size
= newsize
;
vm_object_unlock(prev_object
);
#if defined(DEBUG) || (NDDB > 0)
* vm_object_print: [ debug ]
void vm_object_print(object
, full
)
iprintf("Object 0x%x: size=0x%x, res=%d, ref=%d, ",
(int) object
, (int) object
->size
,
object
->resident_page_count
, object
->ref_count
);
printf("pager=0x%x+0x%x, shadow=(0x%x)+0x%x\n",
(int) object
->pager
, (int) object
->paging_offset
,
(int) object
->shadow
, (int) object
->shadow_offset
);
printf("cache: next=0x%x, prev=0x%x\n",
object
->cached_list
.next
, object
->cached_list
.prev
);
p
= (vm_page_t
) queue_first(&object
->memq
);
while (!queue_end(&object
->memq
, (queue_entry_t
) p
)) {
printf("(off=0x%x,page=0x%x)", p
->offset
, VM_PAGE_TO_PHYS(p
));
p
= (vm_page_t
) queue_next(&p
->listq
);
#endif /* defined(DEBUG) || (NDDB > 0) */