diff --git a/headers/private/kernel/vm_cache.h b/headers/private/kernel/vm_cache.h index 3eeb39d414..26876cd3d9 100755 --- a/headers/private/kernel/vm_cache.h +++ b/headers/private/kernel/vm_cache.h @@ -24,6 +24,7 @@ void vm_cache_release_ref(vm_cache_ref *cache_ref); vm_page *vm_cache_lookup_page(vm_cache_ref *cacheRef, off_t page); void vm_cache_insert_page(vm_cache_ref *cacheRef, vm_page *page, off_t offset); void vm_cache_remove_page(vm_cache_ref *cacheRef, vm_page *page); +status_t vm_cache_set_minimal_commitment(vm_cache_ref *ref, off_t commitment); status_t vm_cache_resize(vm_cache_ref *cacheRef, size_t newSize); status_t vm_cache_insert_region(vm_cache_ref *cacheRef, vm_region *region); status_t vm_cache_remove_region(vm_cache_ref *cacheRef, vm_region *region); diff --git a/headers/private/kernel/vm_page.h b/headers/private/kernel/vm_page.h index be15588dfb..3ea7d95140 100755 --- a/headers/private/kernel/vm_page.h +++ b/headers/private/kernel/vm_page.h @@ -27,6 +27,10 @@ status_t vm_mark_page_inuse(addr_t page); status_t vm_mark_page_range_inuse(addr_t startPage, addr_t length); status_t vm_page_set_state(vm_page *page, int state); +// get some data about the number of pages in the system +addr_t vm_page_num_pages(void); +addr_t vm_page_num_free_pages(void); + vm_page *vm_page_allocate_page(int state); vm_page *vm_page_allocate_page_run(int state, addr_t length); vm_page *vm_page_allocate_specific_page(addr_t page_num, int state); diff --git a/headers/private/kernel/vm_priv.h b/headers/private/kernel/vm_priv.h index 89a4e7f91c..f13365f6b1 100755 --- a/headers/private/kernel/vm_priv.h +++ b/headers/private/kernel/vm_priv.h @@ -37,18 +37,15 @@ #define PAGE_PRESENT 256 // Should only be used by vm internals -int vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isUser, addr_t *newip); -void vm_increase_max_commit(addr_t delta); -int vm_daemon_init(void); +status_t vm_page_fault(addr_t address, addr_t faultAddress, bool isWrite, bool isUser, addr_t *newip); +void vm_unreserve_memory(size_t bytes); +status_t vm_try_reserve_memory(size_t bytes); +status_t vm_daemon_init(void); // used by the page daemon to walk the list of address spaces int vm_aspace_walk_start(struct hash_iterator *i); vm_address_space *vm_aspace_walk_next(struct hash_iterator *i); -// get some data about the number of pages in the system -addr_t vm_page_num_pages(void); -addr_t vm_page_num_free_pages(void); - // allocates memory from the kernel_args structure addr_t vm_alloc_from_kernel_args(kernel_args *args, size_t size, uint32 lock); diff --git a/headers/private/kernel/vm_types.h b/headers/private/kernel/vm_types.h index 91bbe56187..c30a82cd7c 100644 --- a/headers/private/kernel/vm_types.h +++ b/headers/private/kernel/vm_types.h @@ -133,7 +133,7 @@ typedef struct vm_store { // vm_store_ops typedef struct vm_store_ops { void (*destroy)(struct vm_store *backing_store); - off_t (*commit)(struct vm_store *backing_store, off_t size); + status_t (*commit)(struct vm_store *backing_store, off_t size); bool (*has_page)(struct vm_store *backing_store, off_t offset); status_t (*read)(struct vm_store *backing_store, off_t offset, const iovec *vecs, size_t count, size_t *_numBytes); status_t (*write)(struct vm_store *backing_store, off_t offset, const iovec *vecs, size_t count, size_t *_numBytes); diff --git a/src/kernel/core/cache/vnode_store.cpp b/src/kernel/core/cache/vnode_store.cpp index 6fa151f292..57b2b5f508 100644 --- a/src/kernel/core/cache/vnode_store.cpp +++ b/src/kernel/core/cache/vnode_store.cpp @@ -20,13 +20,13 @@ store_destroy(struct vm_store *store) } -static off_t +static status_t store_commit(struct vm_store *_store, off_t size) { vnode_store *store = (vnode_store *)_store; store->vm.committed_size = size; - return size; + return B_OK; } diff --git a/src/kernel/core/vm/vm.c b/src/kernel/core/vm/vm.c index 3ec923021d..2f21510abf 100755 --- a/src/kernel/core/vm/vm.c +++ b/src/kernel/core/vm/vm.c @@ -63,14 +63,14 @@ static region_id next_region_id; static void *region_table; static sem_id region_hash_sem; -static int max_commit; -static spinlock max_commit_lock; +static off_t sAvailableMemory; +static benaphore sAvailableMemoryLock; // function declarations static vm_region *_vm_create_region_struct(vm_address_space *aspace, const char *name, int wiring, int lock); -static int map_backing_store(vm_address_space *aspace, vm_store *store, void **vaddr, +static status_t map_backing_store(vm_address_space *aspace, vm_store *store, void **vaddr, off_t offset, addr_t size, int addr_type, int wiring, int lock, int mapping, vm_region **_region, const char *region_name); -static int vm_soft_fault(addr_t address, bool is_write, bool is_user); +static status_t vm_soft_fault(addr_t address, bool is_write, bool is_user); static vm_region *vm_virtual_map_lookup(vm_virtual_map *map, addr_t address); @@ -448,7 +448,7 @@ insert_area(vm_address_space *addressSpace, void **_address, // a ref to the cache holding this store must be held before entering here -static int +static status_t map_backing_store(vm_address_space *aspace, vm_store *store, void **_virtualAddress, off_t offset, addr_t size, int addressSpec, int wiring, int lock, int mapping, vm_region **_region, const char *region_name) @@ -496,41 +496,12 @@ map_backing_store(vm_address_space *aspace, vm_store *store, void **_virtualAddr cache = nu_cache; cache_ref = cache->ref; store = nu_store; + cache->virtual_size = offset + size; } - mutex_lock(&cache_ref->lock); - // If we don't have enough committed space to cover through to the new end of region... - if (store->committed_size < offset + size) { - // try to commit more memory - off_t old_store_commitment = store->committed_size; // Note what we had - off_t commitment = (store->ops->commit)(store, offset + size); // Commit through to the new end - if (commitment < offset + size) { // Uh oh - didn't work - if (cache->temporary) { - // If this is a temporary cache, Check to see if we ran out of space and return error. - int state = disable_interrupts(); - acquire_spinlock(&max_commit_lock); - - if (max_commit - old_store_commitment + commitment < offset + size) { - release_spinlock(&max_commit_lock); - restore_interrupts(state); - mutex_unlock(&cache_ref->lock); - err = ERR_VM_WOULD_OVERCOMMIT; - goto err1a; - } - - max_commit += (commitment - old_store_commitment) - (offset + size - cache->virtual_size); - cache->virtual_size = offset + size; - release_spinlock(&max_commit_lock); - restore_interrupts(state); - } else { - mutex_unlock(&cache_ref->lock); - err = ENOMEM; - goto err1a; - } - } - } - - mutex_unlock(&cache_ref->lock); + err = vm_cache_set_minimal_commitment(cache_ref, offset + size); + if (err != B_OK) + goto err1a; vm_cache_acquire_ref(cache_ref, true); @@ -565,8 +536,7 @@ map_backing_store(vm_address_space *aspace, vm_store *store, void **_virtualAddr release_sem_etc(aspace->virtual_map.sem, WRITE_COUNT, 0); *_region = region; - - return B_NO_ERROR; + return B_OK; err1b: release_sem_etc(aspace->virtual_map.sem, WRITE_COUNT, 0); @@ -1741,22 +1711,6 @@ vm_delete_areas(struct vm_address_space *aspace) } -static int32 -vm_thread_dump_max_commit(void *unused) -{ - int oldmax = -1; - - (void)(unused); - - for (;;) { - snooze(1000000); - if (oldmax != max_commit) - TRACE(("max_commit 0x%x\n", max_commit)); - oldmax = max_commit; - } -} - - static void unmap_and_free_physical_pages(vm_translation_map *map, addr_t start, addr_t end) { @@ -1946,8 +1900,6 @@ vm_init(kernel_args *args) // initialize some globals next_region_id = 0; region_hash_sem = -1; - max_commit = 0; // will be increased in vm_page_init - max_commit_lock = 0; // map in the new heap and initialize it heap_base = vm_alloc_from_kernel_args(args, HEAP_SIZE, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); @@ -1956,6 +1908,7 @@ vm_init(kernel_args *args) // initialize the free page list and physical page mapper vm_page_init(args); + sAvailableMemory = vm_page_num_pages() * B_PAGE_SIZE; // initialize the hash table that stores the pages mapped to caches vm_cache_init(args); @@ -2037,6 +1990,7 @@ vm_init_post_sem(kernel_args *args) // since we're still single threaded and only the kernel address space exists, // it isn't that hard to find all of the ones we need to create + benaphore_init(&sAvailableMemoryLock, "available memory lock"); arch_vm_translation_map_init_post_sem(args); vm_aspace_init_post_sem(); @@ -2058,12 +2012,6 @@ status_t vm_init_post_thread(kernel_args *args) { vm_page_init_post_thread(args); - - { - thread_id thread = spawn_kernel_thread(&vm_thread_dump_max_commit, "max_commit_thread", B_NORMAL_PRIORITY, NULL); - resume_thread(thread); - } - vm_daemon_init(); return heap_init_post_thread(args); @@ -2088,7 +2036,7 @@ forbid_page_faults(void) } -int +status_t vm_page_fault(addr_t address, addr_t fault_address, bool is_write, bool is_user, addr_t *newip) { int err; @@ -2123,7 +2071,7 @@ vm_page_fault(addr_t address, addr_t fault_address, bool is_write, bool is_user, } -static int +static status_t vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser) { vm_address_space *aspace; @@ -2202,7 +2150,7 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser) // See if this cache has a fault handler - this will do all the work for us if (top_cache_ref->cache->store->ops->fault) { // Note, since the page fault is resolved with interrupts enabled, the - // fault handler could be called more than one for the same reason - + // 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); @@ -2460,17 +2408,30 @@ vm_put_physical_page(addr_t vaddr) void -vm_increase_max_commit(addr_t delta) +vm_unreserve_memory(size_t amount) { - int state; + benaphore_lock(&sAvailableMemoryLock); -// dprintf("vm_increase_max_commit: delta 0x%x\n", delta); + sAvailableMemory += amount; - state = disable_interrupts(); - acquire_spinlock(&max_commit_lock); - max_commit += delta; - release_spinlock(&max_commit_lock); - restore_interrupts(state); + benaphore_unlock(&sAvailableMemoryLock); +} + + +status_t +vm_try_reserve_memory(size_t amount) +{ + status_t status; + benaphore_lock(&sAvailableMemoryLock); + + if (sAvailableMemory > amount) { + sAvailableMemory -= amount; + status = B_OK; + } else + status = B_NO_MEMORY; + + benaphore_unlock(&sAvailableMemoryLock); + return status; } @@ -2725,8 +2686,8 @@ resize_area(area_id areaID, size_t newSize) { vm_cache_ref *cache; vm_region *area, *current; + status_t status = B_OK; size_t oldSize; - bool failed = false; // is newSize a multiple of B_PAGE_SIZE? if (newSize & (B_PAGE_SIZE - 1)) @@ -2751,8 +2712,10 @@ resize_area(area_id areaID, size_t newSize) // We need to check if all areas of this cache can be resized for (current = cache->region_list; current; current = current->cache_next) { - if (current->aspace_next && current->aspace_next->base <= (current->base + newSize)) - goto err; + if (current->aspace_next && current->aspace_next->base <= (current->base + newSize)) { + status = B_ERROR; + goto out; + } } } @@ -2760,7 +2723,7 @@ resize_area(area_id areaID, size_t newSize) for (current = cache->region_list; current; current = current->cache_next) { if (current->aspace_next && current->aspace_next->base <= (current->base + newSize)) { - failed = true; + status = B_ERROR; break; } @@ -2776,23 +2739,20 @@ resize_area(area_id areaID, size_t newSize) } } - if (failed) { + if (status == B_OK) + status = vm_cache_resize(cache, newSize); + + if (status < B_OK) { // This shouldn't really be possible, but hey, who knows for (current = cache->region_list; current; current = current->cache_next) current->size = oldSize; - - goto err; } - - vm_cache_resize(cache, newSize); + +out: mutex_unlock(&cache->lock); // ToDo: we must honour the lock restrictions of this region - return B_OK; - -err: - mutex_unlock(&cache->lock); - return B_ERROR; + return status; } diff --git a/src/kernel/core/vm/vm_cache.c b/src/kernel/core/vm/vm_cache.c index 4883510583..b98bc8a867 100755 --- a/src/kernel/core/vm/vm_cache.c +++ b/src/kernel/core/vm/vm_cache.c @@ -148,7 +148,6 @@ vm_cache_acquire_ref(vm_cache_ref *cache_ref, bool acquire_store_ref) void vm_cache_release_ref(vm_cache_ref *cache_ref) { - off_t store_committed_size = 0; vm_page *page; TRACE(("vm_cache_release_ref: cache_ref 0x%x, ref will be %d\n", cache_ref, cache_ref->ref_count - 1)); @@ -166,10 +165,8 @@ vm_cache_release_ref(vm_cache_ref *cache_ref) // delete this cache // delete the cache's backing store, if it has one - if (cache_ref->cache->store) { - store_committed_size = cache_ref->cache->store->committed_size; + if (cache_ref->cache->store) (*cache_ref->cache->store->ops->destroy)(cache_ref->cache->store); - } // free all of the pages in the cache page = cache_ref->cache->page_list; @@ -191,7 +188,6 @@ vm_cache_release_ref(vm_cache_ref *cache_ref) TRACE(("vm_cache_release_ref: freeing page 0x%x\n", oldPage->ppn)); vm_page_set_state(oldPage, PAGE_STATE_FREE); } - vm_increase_max_commit(cache_ref->cache->virtual_size - store_committed_size); // remove the ref to the source if (cache_ref->cache->source) @@ -289,6 +285,29 @@ vm_cache_remove_page(vm_cache_ref *cache_ref, vm_page *page) } +status_t +vm_cache_set_minimal_commitment(vm_cache_ref *ref, off_t commitment) +{ + status_t status = B_OK; + vm_store *store; + + mutex_lock(&ref->lock); + store = ref->cache->store; + + // If we don't have enough committed space to cover through to the new end of region... + if (store->committed_size < commitment) { + // ToDo: should we check if the cache's virtual size is large + // enough for a commitment of that size? + + // try to commit more memory + status = (store->ops->commit)(store, commitment); + } + + mutex_unlock(&ref->lock); + return status; +} + + /** This function updates the size field of the vm_cache structure. * If needed, it will free up all pages that don't belong to the cache anymore. * The vm_cache_ref lock must be held when you call it. @@ -298,12 +317,19 @@ status_t vm_cache_resize(vm_cache_ref *cacheRef, size_t newSize) { vm_cache *cache = cacheRef->cache; + status_t status; size_t oldSize; + ASSERT_LOCKED_MUTEX(&cacheRef->lock); + // ToDo: only cache's with an anonymous memory store should be resizable! if (!cache->temporary) return B_NOT_ALLOWED; + status = cache->store->ops->commit(cache->store, newSize); + if (status != B_OK) + return status; + oldSize = cache->virtual_size; if (newSize < oldSize) { // we need to remove all pages in the cache outside of the new virtual size @@ -321,8 +347,6 @@ vm_cache_resize(vm_cache_ref *cacheRef, size_t newSize) } cache->virtual_size = newSize; - vm_increase_max_commit(oldSize - newSize); - return B_OK; } diff --git a/src/kernel/core/vm/vm_daemons.c b/src/kernel/core/vm/vm_daemons.c index 4c6e9b011e..f7fad5f908 100755 --- a/src/kernel/core/vm/vm_daemons.c +++ b/src/kernel/core/vm/vm_daemons.c @@ -1,4 +1,7 @@ /* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the Haiku License. +** ** Copyright 2001, Travis Geiselbrecht. All rights reserved. ** Distributed under the terms of the NewOS License. */ @@ -201,7 +204,7 @@ page_daemon(void *unused) } -int +status_t vm_daemon_init() { thread_id thread; diff --git a/src/kernel/core/vm/vm_page.c b/src/kernel/core/vm/vm_page.c index e578f46929..e2cdc47f7d 100755 --- a/src/kernel/core/vm/vm_page.c +++ b/src/kernel/core/vm/vm_page.c @@ -263,9 +263,6 @@ vm_page_init(kernel_args *ka) ka->physical_allocated_range[i].size / B_PAGE_SIZE); } - // set the global max_commit variable - vm_increase_max_commit(num_pages * B_PAGE_SIZE); - TRACE(("vm_page_init: exit\n")); return B_OK; diff --git a/src/kernel/core/vm/vm_store_anonymous_noswap.c b/src/kernel/core/vm/vm_store_anonymous_noswap.c index 413feda6b3..a17327296d 100755 --- a/src/kernel/core/vm/vm_store_anonymous_noswap.c +++ b/src/kernel/core/vm/vm_store_anonymous_noswap.c @@ -1,4 +1,7 @@ /* +** 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. */ @@ -6,6 +9,7 @@ #include #include +#include #include @@ -21,20 +25,28 @@ static void anonymous_destroy(struct vm_store *store) { + vm_unreserve_memory(store->committed_size); free(store); } -/* anonymous_commit - * As this store provides anonymous memory that is simply discarded - * when finished with, we don't bother recording the changes - * here, so we just return 0. - */ - -static off_t +static status_t anonymous_commit(struct vm_store *store, off_t size) { - return 0; + // Check to see how much we could commit - we need real memory + + if (size > store->committed_size) { + // try to commit + if (vm_try_reserve_memory(size - store->committed_size) != B_OK) + return B_NO_MEMORY; + + store->committed_size = size; + } else { + // we can release some + vm_unreserve_memory(store->committed_size - size); + } + + return B_OK; } diff --git a/src/kernel/core/vm/vm_store_device.c b/src/kernel/core/vm/vm_store_device.c index 01bb96d408..5bdbc69a3f 100755 --- a/src/kernel/core/vm/vm_store_device.c +++ b/src/kernel/core/vm/vm_store_device.c @@ -23,11 +23,11 @@ device_destroy(struct vm_store *store) } -static off_t +static status_t device_commit(struct vm_store *store, off_t size) { store->committed_size = size; - return size; + return B_OK; } diff --git a/src/kernel/core/vm/vm_store_null.c b/src/kernel/core/vm/vm_store_null.c index b69facb097..88901a4297 100755 --- a/src/kernel/core/vm/vm_store_null.c +++ b/src/kernel/core/vm/vm_store_null.c @@ -17,11 +17,11 @@ null_destroy(struct vm_store *store) } -static off_t +static status_t null_commit(struct vm_store *store, off_t size) { store->committed_size = size; - return size; + return B_OK; }