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:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user