* 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.9 (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.
#include <vm/vm_pageout.h>
* 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.
* Locate sufficient space in the map. This will give us the
* final virtual address for the new memory, and thus will tell
* us the offset within the kernel map.
if (vm_map_findspace(map
, 0, size
, &addr
)) {
offset
= addr
- VM_MIN_KERNEL_ADDRESS
;
vm_object_reference(kernel_object
);
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");
* Locate sufficient space in the map. This will give us the
* final virtual address for the new memory, and thus will tell
* us the offset within the kernel map.
if (vm_map_findspace(map
, 0, size
, &addr
)) {
if (canwait
) /* XXX should wait */
panic("kmem_malloc: %s too small",
map
== kmem_map
? "kmem_map" : "mb_map");
offset
= addr
- vm_map_min(kmem_map
);
vm_object_reference(kmem_object
);
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.
if (vm_map_findspace(map
, 0, size
, &addr
) == 0)
/* no space now; see if we can ever get space */
if (vm_map_max(map
) - vm_map_min(map
) < size
) {
assert_wait((int)map
, TRUE
);
vm_map_insert(map
, NULL
, (vm_offset_t
)0, addr
, addr
+ size
);
* 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
));
* Create the kernel map; insert a mapping covering kernel text, data, bss,
* and all space allocated thus far (`boostrap' data). The new map will thus
* map the range between VM_MIN_KERNEL_ADDRESS and `start' as allocated, and
* the range between `start' and `end' as free.
void kmem_init(start
, end
)
m
= vm_map_create(kernel_pmap
, VM_MIN_KERNEL_ADDRESS
, end
, FALSE
);
/* N.B.: cannot use kgdb to debug, starting with this assignment ... */
(void) vm_map_insert(m
, NULL
, (vm_offset_t
)0,
VM_MIN_KERNEL_ADDRESS
, start
);
/* ... and ending with the completion of the above `insert' */