* 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.
* %sccs.include.redist.c%
* @(#)vm_kern.c 7.5 (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.
* Kernel memory management.
* Allocate pageable memory to the kernel's address map.
* map must be "kernel_map" below.
vm_offset_t
kmem_alloc_pageable(map
, size
)
panic("kmem_alloc_pageable: not called with kernel_map");
result
= vm_map_find(map
, NULL
, (vm_offset_t
) 0,
if (result
!= KERN_SUCCESS
) {
* Allocate wired-down memory in the kernel's address map
vm_offset_t
kmem_alloc(map
, size
)
register vm_offset_t offset
;
extern vm_object_t kernel_object
;
* Use the kernel object for wired-down kernel pages.
* Assume that no region of the kernel object is
* referenced more than once.
result
= vm_map_find(map
, NULL
, (vm_offset_t
) 0,
if (result
!= KERN_SUCCESS
) {
* Since we didn't know where the new region would
* start, we couldn't supply the correct offset into
* the kernel object. Re-allocate that address
* region with the correct offset.
offset
= addr
- VM_MIN_KERNEL_ADDRESS
;
vm_object_reference(kernel_object
);
vm_map_delete(map
, addr
, addr
+ size
);
vm_map_insert(map
, kernel_object
, offset
, addr
, addr
+ size
);
* Guarantee that there are pages already in this object
* before calling vm_map_pageable. This is to prevent the
* 1) Threads have swapped out, so that there is a
* pager for the kernel_object.
* 2) The kmsg zone is empty, and so we are kmem_allocing
* 3) vm_map_pageable calls vm_fault; there is no page,
* but there is a pager, so we call
* pager_data_request. But the kmsg zone is empty,
* 5) Even if the kmsg zone is not empty: when we get
* the data back from the pager, it will be (very
* stale) non-zero data. kmem_alloc is defined to
* return zero-filled memory.
* We're intentionally not activating the pages we allocate
* to prevent a race with page-out. vm_map_pageable will wire
vm_object_lock(kernel_object
);
for (i
= 0 ; i
< size
; i
+= PAGE_SIZE
) {
while ((mem
= vm_page_alloc(kernel_object
, offset
+i
)) == NULL
) {
vm_object_unlock(kernel_object
);
vm_object_lock(kernel_object
);
vm_object_unlock(kernel_object
);
* And finally, mark the data as non-pageable.
(void) vm_map_pageable(map
, (vm_offset_t
) addr
, addr
+ size
, FALSE
);
* Try to coalesce the map
vm_map_simplify(map
, addr
);
* Release a region of kernel virtual memory allocated
* with kmem_alloc, and return the physical pages
* associated with that region.
void kmem_free(map
, addr
, size
)
register vm_offset_t addr
;
(void) vm_map_remove(map
, trunc_page(addr
), round_page(addr
+ size
));
* Allocates a map to manage a subrange
* of the kernel virtual address space.
* Arguments are as follows:
* parent Map to take range from
* size Size of range to find
* min, max Returned endpoints of map
* pageable Can the region be paged
vm_map_t
kmem_suballoc(parent
, min
, max
, size
, pageable
)
register vm_map_t parent
;
*min
= (vm_offset_t
) vm_map_min(parent
);
ret
= vm_map_find(parent
, NULL
, (vm_offset_t
) 0,
if (ret
!= KERN_SUCCESS
) {
printf("kmem_suballoc: bad status return of %d.\n", ret
);
pmap_reference(vm_map_pmap(parent
));
result
= vm_map_create(vm_map_pmap(parent
), *min
, *max
, pageable
);
panic("kmem_suballoc: cannot create submap");
if ((ret
= vm_map_submap(parent
, *min
, *max
, result
)) != KERN_SUCCESS
)
panic("kmem_suballoc: unable to change range to submap");
* Move memory from source to destination map, possibly deallocating
* the source map reference to the memory.
* Parameters are as follows:
* src_map Source address map
* src_addr Address within source map
* dst_map Destination address map
* num_bytes Amount of data (in bytes) to copy/move
* src_dealloc Should source be removed after copy?
* Assumes the src and dst maps are not already locked.
* Returns new destination address or 0 (if a failure occurs).
vm_offset_t
vm_move(src_map
,src_addr
,dst_map
,num_bytes
,src_dealloc
)
register vm_offset_t src_addr
;
register vm_map_t dst_map
;
register vm_offset_t src_start
; /* Beginning of region */
register vm_size_t src_size
; /* Size of rounded region */
vm_offset_t dst_start
; /* destination address */
* Page-align the source region
src_start
= trunc_page(src_addr
);
src_size
= round_page(src_addr
+ num_bytes
) - src_start
;
* If there's no destination, we can be at most deallocating
if (vm_deallocate(src_map
, src_start
, src_size
)
printf("vm_move: deallocate of source");
printf(" failed, dealloc_only clause\n");
* Allocate a place to put the copy
dst_start
= (vm_offset_t
) 0;
if ((result
= vm_allocate(dst_map
, &dst_start
, src_size
, TRUE
))
* Perform the copy, asking for deallocation if desired
result
= vm_map_copy(dst_map
, src_map
, dst_start
, src_size
,
src_start
, FALSE
, src_dealloc
);
* Return the destination address corresponding to
* the source address given (rather than the front
* of the newly-allocated page).
if (result
== KERN_SUCCESS
)
return(dst_start
+ (src_addr
- src_start
));
* Allocate wired-down memory in the kernel's address map for the higher
* level kernel memory allocator (kern/kern_malloc.c). We cannot use
* kmem_alloc() because we may need to allocate memory at interrupt
* level where we cannot block (canwait == FALSE).
* This routine has its own private kernel submap (kmem_map) and object
* (kmem_object). This, combined with the fact that only malloc uses
* this routine, ensures that we will never block in map or object waits.
* Note that this still only works in a uni-processor environment and
* when called at splhigh().
* We don't worry about expanding the map (adding entries) since entries
* for wired maps are statically allocated.
kmem_malloc(map
, size
, canwait
)
register vm_offset_t offset
, i
;
extern vm_object_t kmem_object
;
if (map
!= kmem_map
&& map
!= mb_map
)
panic("kern_malloc_alloc: map != {kmem,mb}_map");
if (vm_map_find(map
, NULL
, (vm_offset_t
)0,
&addr
, size
, TRUE
) != KERN_SUCCESS
) {
panic("kmem_malloc: kmem_map too small");
* Since we didn't know where the new region would start,
* we couldn't supply the correct offset into the kmem object.
* Re-allocate that address region with the correct offset.
offset
= addr
- vm_map_min(kmem_map
);
vm_object_reference(kmem_object
);
vm_map_delete(map
, addr
, addr
+ size
);
vm_map_insert(map
, kmem_object
, offset
, addr
, addr
+ size
);
* If we can wait, just mark the range as wired
* (will fault pages as necessary).
(void) vm_map_pageable(map
, (vm_offset_t
) addr
, addr
+ size
,
vm_map_simplify(map
, addr
);
* If we cannot wait then we must allocate all memory up front,
* pulling it off the active queue to prevent pageout.
vm_object_lock(kmem_object
);
for (i
= 0; i
< size
; i
+= PAGE_SIZE
) {
m
= vm_page_alloc(kmem_object
, offset
+ i
);
* Ran out of space, free everything up and return.
* Don't need to lock page queues here as we know
* that the pages we got aren't on any queues.
m
= vm_page_lookup(kmem_object
, offset
+ i
);
vm_object_unlock(kmem_object
);
vm_map_delete(map
, addr
, addr
+ size
);
vm_object_unlock(kmem_object
);
* Mark map entry as non-pageable.
* Assert: vm_map_insert() will never be able to extend the previous
* entry so there will be a new entry exactly corresponding to this
* address range and it will have wired_count == 0.
if (!vm_map_lookup_entry(map
, addr
, &entry
) ||
entry
->start
!= addr
|| entry
->end
!= addr
+ size
||
panic("kmem_malloc: entry not found or misaligned");
* Loop thru pages, entering them in the pmap.
* (We cannot add them to the wired count without
* wrapping the vm_page_queue_lock in splimp...)
for (i
= 0; i
< size
; i
+= PAGE_SIZE
) {
vm_object_lock(kmem_object
);
m
= vm_page_lookup(kmem_object
, offset
+ i
);
vm_object_unlock(kmem_object
);
pmap_enter(map
->pmap
, addr
+ i
, VM_PAGE_TO_PHYS(m
),
vm_map_simplify(map
, addr
);
* Allocates pageable memory from a sub-map of the kernel. If the submap
* has no room, the caller sleeps waiting for more memory in the submap.
vm_offset_t
kmem_alloc_wait(map
, size
)
* To make this work for more than one map,
* use the map's lock to lock out sleepers/wakers.
* Unfortunately, vm_map_find also grabs the map lock.
lock_set_recursive(&map
->lock
);
result
= vm_map_find(map
, NULL
, (vm_offset_t
) 0,
lock_clear_recursive(&map
->lock
);
if (result
!= KERN_SUCCESS
) {
if ( (vm_map_max(map
) - vm_map_min(map
)) < size
) {
assert_wait((int)map
, TRUE
);
} while (result
!= KERN_SUCCESS
);
* Returns memory to a submap of the kernel, and wakes up any threads
* waiting for memory in that map.
void kmem_free_wakeup(map
, addr
, size
)
(void) vm_map_delete(map
, trunc_page(addr
), round_page(addr
+ size
));
* Initialize the kernel's virtual memory map, taking
* into account all memory allocated up to this time.
void kmem_init(start
, end
)
extern vm_map_t kernel_map
;
addr
= VM_MIN_KERNEL_ADDRESS
;
kernel_map
= vm_map_create(kernel_pmap
, addr
, end
, FALSE
);
(void) vm_map_find(kernel_map
, NULL
, (vm_offset_t
) 0,
&addr
, (start
- VM_MIN_KERNEL_ADDRESS
),