From 8bde4cf98c9fa0fe70791ac6123375cccacd647c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 18 Nov 2004 18:03:34 +0000 Subject: [PATCH] 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 --- src/kernel/core/vm/vm.c | 73 ++++++++----- .../core/vm/vm_store_anonymous_noswap.c | 100 ++++++++++++++---- 2 files changed, 121 insertions(+), 52 deletions(-) diff --git a/src/kernel/core/vm/vm.c b/src/kernel/core/vm/vm.c index e7653fcc11..145fb0374f 100755 --- a/src/kernel/core/vm/vm.c +++ b/src/kernel/core/vm/vm.c @@ -1,10 +1,10 @@ /* -** Copyright 2002-2004, Axel Dörfler, axeld@pinc-software.de. 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, axeld@pinc-software.de. + * 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 @@ -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); diff --git a/src/kernel/core/vm/vm_store_anonymous_noswap.c b/src/kernel/core/vm/vm_store_anonymous_noswap.c index a17327296d..3e51ad490e 100755 --- a/src/kernel/core/vm/vm_store_anonymous_noswap.c +++ b/src/kernel/core/vm/vm_store_anonymous_noswap.c @@ -1,26 +1,37 @@ /* -** Copyright 2002-2004, Axel Dörfler, axeld@pinc-software.de. 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, axeld@pinc-software.de. + * 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 #include #include +#include #include -//#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; }