Treat the addr argument for mmap(2) request without MAP_FIXED flag as

a hint.

Right now, for non-fixed mmap(2) calls, addr is de-facto interpreted
as the absolute minimal address of the range where the mapping is
created.  The VA allocator only allocates in the range [addr,
VM_MAXUSER_ADDRESS].  This is too restrictive, the mmap(2) call might
unduly fail if there is no free addresses above addr but a lot of
usable space below it.

Lift this implementation limitation by allocating VA in two passes.
First, try to allocate above addr, as before.  If that fails, do the
second pass with less restrictive constraints for the start of
allocation by specifying minimal allocation address at the max bss
end, if this limit is less than addr.

One important case where this change makes a difference is the
allocation of the stacks for new threads in libthr.  Under some
configuration conditions, libthr tries to hint kernel to reuse the
main thread stack grow area for the new stacks.  This cannot work by
design now after grow area is converted to stack, and there is no
unallocated VA above the main stack.  Interpreting requested stack
base address as the hint provides compatibility with old libthr and
with (mis-)configured current libthr.

Reviewed by:	alc
Tested by:	dim (previous version)
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2017-06-28 04:02:36 +00:00
parent 3fe0d81e1f
commit 6a97a3f756
3 changed files with 37 additions and 5 deletions

View File

@ -1556,6 +1556,25 @@ vm_map_find(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
return (result);
}
int
vm_map_find_min(vm_map_t map, vm_object_t object, vm_ooffset_t offset,
vm_offset_t *addr, vm_size_t length, vm_offset_t min_addr,
vm_offset_t max_addr, int find_space, vm_prot_t prot, vm_prot_t max,
int cow)
{
vm_offset_t hint;
int rv;
hint = *addr;
for (;;) {
rv = vm_map_find(map, object, offset, addr, length, max_addr,
find_space, prot, max, cow);
if (rv == KERN_SUCCESS || min_addr >= hint)
return (rv);
*addr = min_addr;
}
}
/*
* vm_map_simplify_entry:
*

View File

@ -372,6 +372,8 @@ vm_map_t vm_map_create(pmap_t, vm_offset_t, vm_offset_t);
int vm_map_delete(vm_map_t, vm_offset_t, vm_offset_t);
int vm_map_find(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t *, vm_size_t,
vm_offset_t, int, vm_prot_t, vm_prot_t, int);
int vm_map_find_min(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t *,
vm_size_t, vm_offset_t, vm_offset_t, int, vm_prot_t, vm_prot_t, int);
int vm_map_fixed(vm_map_t, vm_object_t, vm_ooffset_t, vm_offset_t, vm_size_t,
vm_prot_t, vm_prot_t, int);
int vm_map_findspace (vm_map_t, vm_offset_t, vm_size_t, vm_offset_t *);

View File

@ -1438,10 +1438,12 @@ vm_mmap_object(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot,
vm_prot_t maxprot, int flags, vm_object_t object, vm_ooffset_t foff,
boolean_t writecounted, struct thread *td)
{
boolean_t fitit;
boolean_t curmap, fitit;
vm_offset_t max_addr;
int docow, error, findspace, rv;
if (map == &td->td_proc->p_vmspace->vm_map) {
curmap = map == &td->td_proc->p_vmspace->vm_map;
if (curmap) {
PROC_LOCK(td->td_proc);
if (map->size + size > lim_cur_proc(td->td_proc, RLIMIT_VMEM)) {
PROC_UNLOCK(td->td_proc);
@ -1529,11 +1531,20 @@ vm_mmap_object(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot,
MAP_ALIGNMENT_SHIFT);
else
findspace = VMFS_OPTIMAL_SPACE;
rv = vm_map_find(map, object, foff, addr, size,
max_addr = 0;
#ifdef MAP_32BIT
flags & MAP_32BIT ? MAP_32BIT_MAX_ADDR :
if ((flags & MAP_32BIT) != 0)
max_addr = MAP_32BIT_MAX_ADDR;
#endif
0, findspace, prot, maxprot, docow);
if (curmap) {
rv = vm_map_find_min(map, object, foff, addr, size,
round_page((vm_offset_t)td->td_proc->p_vmspace->
vm_daddr + lim_max(td, RLIMIT_DATA)), max_addr,
findspace, prot, maxprot, docow);
} else {
rv = vm_map_find(map, object, foff, addr, size,
max_addr, findspace, prot, maxprot, docow);
}
} else {
rv = vm_map_fixed(map, object, foff, *addr, size,
prot, maxprot, docow);