diff options
author | alc <alc@FreeBSD.org> | 2010-10-04 16:49:40 +0000 |
---|---|---|
committer | alc <alc@FreeBSD.org> | 2010-10-04 16:49:40 +0000 |
commit | b0606e2f168c4625960b95c7a9b0e6d6c278faa2 (patch) | |
tree | 2c98870dcc5c73356645845656f267b9e238f9a7 | |
parent | 09583f57805b64642c59c88c3b2049090f307df7 (diff) | |
download | FreeBSD-src-b0606e2f168c4625960b95c7a9b0e6d6c278faa2.zip FreeBSD-src-b0606e2f168c4625960b95c7a9b0e6d6c278faa2.tar.gz |
If vm_map_find() is asked to allocate a superpage-aligned region of virtual
addresses that is greater than a superpage in size but not a multiple of
the superpage size, then vm_map_find() is not always expanding the kernel
pmap to support the last few small pages being allocated. These failures
are not commonplace, so this was first noticed by someone porting FreeBSD
to a new architecture. Previously, we grew the kernel page table in
vm_map_findspace() when we found the first available virtual address.
This works most of the time because we always grow the kernel pmap or page
table by an amount that is a multiple of the superpage size. Now, instead,
we defer the call to pmap_growkernel() until we are committed to a range
of virtual addresses in vm_map_insert(). In general, there is another
reason to prefer calling pmap_growkernel() in vm_map_insert(). It makes
it possible for someone to do the equivalent of an mmap(MAP_FIXED) on the
kernel map.
Reported by: Svatopluk Kraus
Reviewed by: kib@
MFC after: 3 weeks
-rw-r--r-- | sys/vm/vm_map.c | 22 |
1 files changed, 8 insertions, 14 deletions
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index c934db7..2e0f001 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -1163,6 +1163,9 @@ vm_map_insert(vm_map_t map, vm_object_t object, vm_ooffset_t offset, } charged: + /* Expand the kernel pmap, if necessary. */ + if (map == kernel_map && end > kernel_vm_end) + pmap_growkernel(end); if (object != NULL) { /* * OBJ_ONEMAPPING must be cleared unless this mapping @@ -1299,7 +1302,7 @@ vm_map_findspace(vm_map_t map, vm_offset_t start, vm_size_t length, vm_offset_t *addr) /* OUT */ { vm_map_entry_t entry; - vm_offset_t end, st; + vm_offset_t st; /* * Request must fit within min/max VM address and must avoid @@ -1313,7 +1316,7 @@ vm_map_findspace(vm_map_t map, vm_offset_t start, vm_size_t length, /* Empty tree means wide open address space. */ if (map->root == NULL) { *addr = start; - goto found; + return (0); } /* @@ -1323,7 +1326,7 @@ vm_map_findspace(vm_map_t map, vm_offset_t start, vm_size_t length, map->root = vm_map_entry_splay(start, map->root); if (start + length <= map->root->start) { *addr = start; - goto found; + return (0); } /* @@ -1334,7 +1337,7 @@ vm_map_findspace(vm_map_t map, vm_offset_t start, vm_size_t length, st = (start > map->root->end) ? start : map->root->end; if (length <= map->root->end + map->root->adj_free - st) { *addr = st; - goto found; + return (0); } /* With max_free, can immediately tell if no solution. */ @@ -1352,22 +1355,13 @@ vm_map_findspace(vm_map_t map, vm_offset_t start, vm_size_t length, entry = entry->left; else if (entry->adj_free >= length) { *addr = entry->end; - goto found; + return (0); } else entry = entry->right; } /* Can't get here, so panic if we do. */ panic("vm_map_findspace: max_free corrupt"); - -found: - /* Expand the kernel pmap, if necessary. */ - if (map == kernel_map) { - end = round_page(*addr + length); - if (end > kernel_vm_end) - pmap_growkernel(end); - } - return (0); } int |