First steps towards being able of collapse vm_cache objects after forking:

* a vm_cache now maintains a list of its "consumer" caches.
* introduced to new functions that add/remove consumer to a cache (instead
  of only maintaining the vm_cache::source field).
* fixed the incorrect reference counting when doing copy-on-write; we kept
  one ref too many of the lower cache.
* minor cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19035 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-10-10 17:16:06 +00:00
parent 6177d112d4
commit d593e74a27
4 changed files with 66 additions and 13 deletions

View File

@ -27,6 +27,8 @@ 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);
void vm_cache_remove_consumer(vm_cache_ref *cacheRef, vm_cache *consumer);
void vm_cache_add_consumer(vm_cache_ref *cacheRef, vm_cache *consumer);
status_t vm_cache_write_modified(vm_cache_ref *ref, bool fsReenter);
status_t vm_cache_set_minimal_commitment(vm_cache_ref *ref, off_t commitment);
status_t vm_cache_resize(vm_cache_ref *cacheRef, off_t newSize);

View File

@ -68,6 +68,9 @@ typedef struct vm_cache_ref {
// vm_cache
typedef struct vm_cache {
struct list_link consumer_link;
struct list consumers;
// list of caches that use this cache as a source
vm_page *page_list;
vm_cache_ref *ref;
struct vm_cache *source;

View File

@ -552,11 +552,10 @@ map_backing_store(vm_address_space *addressSpace, vm_store *store, void **_virtu
newCache->temporary = 1;
newCache->scan_skip = cache->scan_skip;
newCache->source = cache;
vm_cache_acquire_ref(cacheRef);
vm_cache_add_consumer(cacheRef, newCache);
cache = newCache;
cacheRef = cache->ref;
cacheRef = newCache->ref;
store = newStore;
cache->virtual_size = offset + size;
}
@ -1479,13 +1478,12 @@ vm_copy_on_write_area(vm_area *area)
upperCache->temporary = 1;
upperCache->scan_skip = lowerCache->scan_skip;
upperCache->source = lowerCache;
list_add_item(&lowerCache->consumers, upperCache);
upperCache->ref = upperCacheRef;
upperCacheRef->cache = upperCache;
// we need to manually alter the ref_count
// ToDo: investigate a bit deeper if this is really correct
// (doesn't look like it, but it works)
lowerCacheRef->ref_count = upperCacheRef->ref_count;
// we need to manually alter the ref_count (divide it between the two)
lowerCacheRef->ref_count = upperCacheRef->ref_count - 1;
upperCacheRef->ref_count = 1;
// grab a ref to the cache object we're now linked to as a source

View File

@ -20,7 +20,8 @@
#include <smp.h>
#include <arch/cpu.h>
#include <malloc.h>
#include <stddef.h>
#include <stdlib.h>
//#define TRACE_VM_CACHE
#ifdef TRACE_VM_CACHE
@ -73,11 +74,9 @@ page_hash_func(void *_p, const void *_key, uint32 range)
status_t
vm_cache_init(kernel_args *ka)
vm_cache_init(kernel_args *args)
{
vm_page p;
page_cache_table = hash_init(PAGE_TABLE_SIZE, (int)&p.hash_next - (int)&p,
page_cache_table = hash_init(PAGE_TABLE_SIZE, offsetof(vm_page, hash_next),
&page_compare_func, &page_hash_func);
if (!page_cache_table)
panic("vm_cache_init: cannot allocate memory for page cache hash table\n");
@ -101,6 +100,7 @@ vm_cache_create(vm_store *store)
if (cache == NULL)
return NULL;
list_init(&cache->consumers);
cache->page_list = NULL;
cache->ref = NULL;
cache->source = NULL;
@ -211,7 +211,7 @@ vm_cache_release_ref(vm_cache_ref *cache_ref)
// remove the ref to the source
if (cache_ref->cache->source)
vm_cache_release_ref(cache_ref->cache->source->ref);
vm_cache_remove_consumer(cache_ref->cache->source->ref, cache_ref->cache);
mutex_destroy(&cache_ref->lock);
free(cache_ref->cache);
@ -391,6 +391,56 @@ vm_cache_resize(vm_cache_ref *cacheRef, off_t newSize)
}
/*!
Removes the \a consumer from the \a cacheRef's cache.
It will also release the reference to the cacheRef owned by the consumer.
*/
void
vm_cache_remove_consumer(vm_cache_ref *cacheRef, vm_cache *consumer)
{
vm_cache *cache = cacheRef->cache;
TRACE(("remove consumer vm cache %p from cache %p\n", consumer, cache));
mutex_lock(&cacheRef->lock);
list_remove_item(&cache->consumers, consumer);
consumer->source = NULL;
if (cacheRef->areas == NULL && cache->source != NULL
&& !list_is_empty(&cache->consumers)
&& cache->consumers.link.next == cache->consumers.link.prev) {
// The cache is not really needed anymore - it can be merged with its only
// consumer left.
// TODO!
TRACE(("merge vm cache %p (ref == %ld) with vm cache %p\n",
cache, cacheRef->ref_count, cache->consumers.link.next));
}
mutex_unlock(&cacheRef->lock);
vm_cache_release_ref(cacheRef);
}
/*!
Marks the \a cacheRef's cache as source of the \a consumer cache,
and adds the \a consumer to its list.
This also grabs a reference to the source cache.
*/
void
vm_cache_add_consumer(vm_cache_ref *cacheRef, vm_cache *consumer)
{
TRACE(("add consumer vm cache %p to cache %p\n", consumer, cacheRef->cache));
mutex_lock(&cacheRef->lock);
consumer->source = cacheRef->cache;
list_add_item(&cacheRef->cache->consumers, consumer);
mutex_unlock(&cacheRef->lock);
vm_cache_acquire_ref(cacheRef);
}
status_t
vm_cache_insert_area(vm_cache_ref *cache_ref, vm_area *area)
{