Changed the way the stores commit their memory: there is no special handling

for temporary memory anymore, it's the store's responsibility to do that
correctly now, and that functionality is reached via the vm_cache using
vm_cache_set_minimal_commitment().
Therefore, the vm_store commit() function now returns a status instead of
the size that could be commited.
Replaced the max_commit mechanism with one that cares about the available
memory, stores can reserve and unreserve such memory. The anonymous_commit()
will now fail in case it could not reserve the needed amount of memory.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9777 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2004-11-03 17:24:41 +00:00
parent 1583190bd8
commit ba44a1e8e1
12 changed files with 120 additions and 122 deletions
+1
View File
@@ -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);
+4
View File
@@ -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);
+4 -7
View File
@@ -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);
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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;
}
+49 -89
View File
@@ -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;
}
+31 -7
View File
@@ -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;
}
+4 -1
View File
@@ -1,4 +1,7 @@
/*
** Copyright 2004, Axel Dörfler, [email protected]. 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;
-3
View File
@@ -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;
+20 -8
View File
@@ -1,4 +1,7 @@
/*
** 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.
*/
@@ -6,6 +9,7 @@
#include <KernelExport.h>
#include <vm_store_anonymous_noswap.h>
#include <vm_priv.h>
#include <stdlib.h>
@@ -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;
}
+2 -2
View File
@@ -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;
}
+2 -2
View File
@@ -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;
}