vm_store::fault() can now decide not to handle the fault - when it returns

B_BAD_HANDLER, vm_soft_fault() will just continue as if there is no fault()
function.
There is now support for the B_STACK_AREA and B_KERNEL_STACK_AREA protection
values: if the former is used, or the latter in combination with DEBUG_KERNEL_STACKS,
a number of guard pages is inserted at the bottom of the stack (depending on
the STACK_GROWS_DOWNWARDS/UPWARDS arch config).
In addition, user stacks are no longer committed completely, ie. they don't
reserve memory for their whole size, only as much as is needed. That may
result in applications crashing when they need a lot of stack and there is
no memory left. Before this change, you could not even run that application.
Since BeOS has a 16 MB stack for the main thread, you could only run about
2 applications with 64 MB before.
Due to these changes, we've now switched to the standard BeOS stack sizes.
Some minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@10014 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2004-11-18 18:03:34 +00:00
parent 9ebc403c77
commit 8bde4cf98c
2 changed files with 121 additions and 52 deletions
+44 -29
View File
@@ -1,10 +1,10 @@
/*
** Copyright 2002-2004, Axel Dörfler, [email protected]. All rights reserved.
** Distributed under the terms of the Haiku License.
**
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
* Copyright 2002-2004, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <OS.h>
@@ -446,7 +446,7 @@ map_backing_store(vm_address_space *aspace, vm_store *store, void **_virtualAddr
// pair to handle the private copies of pages as they are written to
if (mapping == REGION_PRIVATE_MAP) {
// create an anonymous store object
nu_store = vm_store_create_anonymous_noswap();
nu_store = vm_store_create_anonymous_noswap((protection & B_STACK_AREA) != 0, USER_STACK_GUARD_PAGES);
if (nu_store == NULL)
panic("map_backing_store: vm_create_store_anonymous_noswap returned NULL");
nu_cache = vm_cache_create(nu_store);
@@ -632,10 +632,16 @@ vm_create_anonymous_area(aspace_id aid, const char *name, void **address,
vm_address_space *aspace;
vm_cache_ref *cache_ref;
vm_page *page = NULL;
bool isStack = (protection & B_STACK_AREA) != 0;
status_t err;
TRACE(("create_anonymous_area %s: size 0x%lx\n", name, size));
#ifdef DEBUG_KERNEL_STACKS
if ((protection & B_KERNEL_STACK_AREA) != 0)
isStack = true;
#endif
/* check parameters */
switch (addressSpec) {
case B_ANY_ADDRESS:
@@ -680,7 +686,8 @@ vm_create_anonymous_area(aspace_id aid, const char *name, void **address,
}
// create an anonymous store object
store = vm_store_create_anonymous_noswap();
store = vm_store_create_anonymous_noswap(isStack, (protection & B_USER_PROTECTION) != 0 ?
USER_STACK_GUARD_PAGES : KERNEL_STACK_GUARD_PAGES);
if (store == NULL)
panic("vm_create_anonymous_area: vm_create_store_anonymous_noswap returned NULL");
cache = vm_cache_create(store);
@@ -742,7 +749,14 @@ vm_create_anonymous_area(aspace_id aid, const char *name, void **address,
addr_t va;
// XXX remove
for (va = area->base; va < area->base + area->size; va += B_PAGE_SIZE) {
// dprintf("mapping wired pages: area 0x%x, cache_ref 0x%x 0x%x\n", area, cache_ref, area->cache_ref);
#ifdef DEBUG_KERNEL_STACKS
# ifdef STACK_GROWS_DOWNWARDS
if (isStack && va < area->base + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE)
# else
if (isStack && va >= area->base + area->size - KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE)
# endif
continue;
#endif
vm_soft_fault(va, false, false);
}
break;
@@ -1027,13 +1041,13 @@ _user_vm_map_file(const char *uname, void **uaddress, int addressSpec,
addr_t size, int protection, int mapping, const char *upath, off_t offset)
{
char name[B_OS_NAME_LENGTH];
char path[B_PATH_NAME_LENGTH];
void *address;
char path[SYS_MAX_PATH_LEN];
int rc;
if (!IS_USER_ADDRESS(uname) || !IS_USER_ADDRESS(uaddress) || !IS_USER_ADDRESS(upath)
|| user_strlcpy(name, uname, B_OS_NAME_LENGTH) < B_OK
|| user_strlcpy(path, upath, SYS_MAX_PATH_LEN) < B_OK
|| user_strlcpy(path, upath, B_PATH_NAME_LENGTH) < B_OK
|| user_memcpy(&address, uaddress, sizeof(address)) < B_OK)
return B_BAD_ADDRESS;
@@ -1220,7 +1234,7 @@ vm_copy_on_write_area(vm_area *area)
lowerCache = upperCacheRef->cache;
// create an anonymous store object
store = vm_store_create_anonymous_noswap();
store = vm_store_create_anonymous_noswap(false, 0);
if (store == NULL)
return B_NO_MEMORY;
@@ -2068,7 +2082,7 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
if (aspace == NULL) {
if (isUser == false) {
dprintf("vm_soft_fault: kernel thread accessing invalid user memory!\n");
return ERR_VM_PF_FATAL;
return B_BAD_ADDRESS;
} else {
// XXX weird state.
panic("vm_soft_fault: non kernel thread accessing user memory that doesn't exist!\n");
@@ -2077,7 +2091,7 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
} else {
// the hit was probably in the 64k DMZ between kernel and user space
// this keeps a user space thread from passing a buffer that crosses into kernel space
return ERR_VM_PF_FATAL;
return B_BAD_ADDRESS;
}
map = &aspace->virtual_map;
atomic_add(&aspace->fault_count, 1);
@@ -2090,7 +2104,7 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
release_sem_etc(map->sem, READ_COUNT, 0);
vm_put_aspace(aspace);
dprintf("vm_soft_fault: va 0x%lx not covered by area in address space\n", originalAddress);
return ERR_VM_PF_BAD_ADDRESS; // BAD_ADDRESS
return B_BAD_ADDRESS;
}
// check permissions
@@ -2098,13 +2112,13 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
release_sem_etc(map->sem, READ_COUNT, 0);
vm_put_aspace(aspace);
dprintf("user access on kernel area 0x%lx at %p\n", area->id, (void *)originalAddress);
return ERR_VM_PF_BAD_PERM; // BAD_PERMISSION
return B_PERMISSION_DENIED;
}
if (isWrite && (area->protection & (B_WRITE_AREA | (isUser ? 0 : B_KERNEL_WRITE_AREA))) == 0) {
release_sem_etc(map->sem, READ_COUNT, 0);
vm_put_aspace(aspace);
dprintf("write access attempted on read-only area 0x%lx at %p\n", area->id, (void *)originalAddress);
return ERR_VM_PF_BAD_PERM; // BAD_PERMISSION
return B_PERMISSION_DENIED;
}
// We have the area, it was a valid access, so let's try to resolve the page fault now.
@@ -2117,14 +2131,16 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
release_sem_etc(map->sem, READ_COUNT, 0);
// See if this cache has a fault handler - this will do all the work for us
if (top_cache_ref->cache->store->ops->fault) {
if (top_cache_ref->cache->store->ops->fault != NULL) {
// Note, since the page fault is resolved with interrupts enabled, the
// fault handler could be called more than once for the same reason -
// the store must take this into account
int err = (*top_cache_ref->cache->store->ops->fault)(top_cache_ref->cache->store, aspace, cache_offset);
vm_cache_release_ref(top_cache_ref);
vm_put_aspace(aspace);
return err;
status_t status = (*top_cache_ref->cache->store->ops->fault)(top_cache_ref->cache->store, aspace, cache_offset);
if (status != B_BAD_HANDLER) {
vm_cache_release_ref(top_cache_ref);
vm_put_aspace(aspace);
return status;
}
}
// The top most cache has no fault handler, so let's see if the cache or its sources
@@ -2872,8 +2888,10 @@ _user_get_next_area_info(team_id team, int32 *userCookie, area_info *userInfo)
status_t
_user_set_area_protection(area_id area, uint32 newProtection)
{
// ToDo: implement set_area_protection()
return B_ERROR;
if ((newProtection & ~B_USER_PROTECTION) != 0)
return B_BAD_VALUE;
return set_area_protection(area, newProtection);
}
@@ -2898,7 +2916,7 @@ _user_clone_area(const char *userName, void **userAddress, uint32 addressSpec,
case B_ANY_KERNEL_BLOCK_ADDRESS:
return B_BAD_VALUE;
}
if (protection & B_KERNEL_PROTECTION)
if ((protection & ~B_USER_PROTECTION) != 0)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userName)
@@ -2936,7 +2954,7 @@ _user_create_area(const char *userName, void **userAddress, uint32 addressSpec,
case B_ANY_KERNEL_BLOCK_ADDRESS:
return B_BAD_VALUE;
}
if (protection & B_KERNEL_PROTECTION)
if ((protection & ~B_USER_PROTECTION) != 0)
return B_BAD_VALUE;
if (!IS_USER_ADDRESS(userName)
@@ -2949,9 +2967,6 @@ _user_create_area(const char *userName, void **userAddress, uint32 addressSpec,
&& IS_KERNEL_ADDRESS(address))
return B_BAD_VALUE;
if ((protection & B_KERNEL_PROTECTION) == 0)
protection |= B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA;
area = vm_create_anonymous_area(vm_get_current_user_aspace_id(), (char *)name, &address,
addressSpec, size, lock, protection | B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
+77 -23
View File
@@ -1,26 +1,37 @@
/*
** Copyright 2002-2004, Axel Dörfler, [email protected]. All rights reserved.
** Distributed under the terms of the Haiku License.
**
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
* Copyright 2002-2004, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <KernelExport.h>
#include <vm_store_anonymous_noswap.h>
#include <vm_priv.h>
#include <arch_config.h>
#include <stdlib.h>
//#define TRACE_VM
#ifdef TRACE_VM
//#define TRACE_STORE
#ifdef TRACE_STORE
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// The stack functionality looks like a good candidate to put into its own
// store. I have not done this because once we have a swap file backing up
// the memory, it would probably not a good idea to separate this anymore.
typedef struct anonymous_store {
vm_store vm;
bool is_stack;
int32 guarded_size;
} anonymous_store;
static void
anonymous_destroy(struct vm_store *store)
@@ -31,19 +42,25 @@ anonymous_destroy(struct vm_store *store)
static status_t
anonymous_commit(struct vm_store *store, off_t size)
anonymous_commit(struct vm_store *_store, off_t size)
{
anonymous_store *store = (anonymous_store *)_store;
// if we're a stack, we don't commit here, but in anonymous_fault()
if (store->is_stack)
return B_OK;
// Check to see how much we could commit - we need real memory
if (size > store->committed_size) {
if (size > store->vm.committed_size) {
// try to commit
if (vm_try_reserve_memory(size - store->committed_size) != B_OK)
if (vm_try_reserve_memory(size - store->vm.committed_size) != B_OK)
return B_NO_MEMORY;
store->committed_size = size;
store->vm.committed_size = size;
} else {
// we can release some
vm_unreserve_memory(store->committed_size - size);
vm_unreserve_memory(store->vm.committed_size - size);
}
return B_OK;
@@ -73,15 +90,49 @@ anonymous_write(struct vm_store *store, off_t offset, const iovec *vecs, size_t
}
static status_t
anonymous_fault(struct vm_store *_store, struct vm_address_space *aspace, off_t offset)
{
anonymous_store *store = (anonymous_store *)_store;
if (store->is_stack) {
uint32 guardOffset;
#ifdef STACK_GROWS_DOWNWARDS
guardOffset = 0;
#elif defined(STACK_GROWS_UPWARDS)
guardOffset = store->vm.cache->virtual_size - store->guarded_size;
#else
# error Stack direction has not been defined in arch_config.h
#endif
// report stack fault, guard page hit!
if (offset >= guardOffset && offset < guardOffset + store->guarded_size) {
TRACE(("stack overflow!\n"));
return B_BAD_ADDRESS;
}
// try to commit additional memory
if (vm_try_reserve_memory(B_PAGE_SIZE) != B_OK)
return B_NO_MEMORY;
store->vm.committed_size += B_PAGE_SIZE;
}
// This will cause vm_soft_fault() to handle the fault
return B_BAD_HANDLER;
}
static vm_store_ops anonymous_ops = {
&anonymous_destroy,
&anonymous_commit,
&anonymous_has_page,
&anonymous_read,
&anonymous_write,
NULL, // fault() is unused
NULL,
NULL
&anonymous_fault,
NULL, // acquire ref
NULL // release ref
};
@@ -90,18 +141,21 @@ static vm_store_ops anonymous_ops = {
*/
vm_store *
vm_store_create_anonymous_noswap()
vm_store_create_anonymous_noswap(bool stack, int32 numGuardPages)
{
vm_store *store = malloc(sizeof(vm_store));
anonymous_store *store = malloc(sizeof(anonymous_store));
if (store == NULL)
return NULL;
TRACE(("vm_store_create_anonymous (%p)\n", store));
TRACE(("vm_store_create_anonymous(stack = %s, numGuardPages = %ld) at %p\n",
stack ? "true" : "false", numGuardPages, store));
store->ops = &anonymous_ops;
store->cache = NULL;
store->committed_size = 0;
store->vm.ops = &anonymous_ops;
store->vm.cache = NULL;
store->vm.committed_size = 0;
store->is_stack = stack;
store->guarded_size = numGuardPages * B_PAGE_SIZE;
return store;
return &store->vm;
}