From 4e4c58037e5219bb869d920615447a154545a934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Tue, 10 Oct 2006 22:47:00 +0000 Subject: [PATCH] Implemented collapsing forked vm_cache object chains when they become unused. This fixes bug #227 by closing that memory and semaphore leak. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19041 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/vm/vm_cache.c | 53 +++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/src/system/kernel/vm/vm_cache.c b/src/system/kernel/vm/vm_cache.c index f8540c8e1c..66eb26d274 100644 --- a/src/system/kernel/vm/vm_cache.c +++ b/src/system/kernel/vm/vm_cache.c @@ -398,24 +398,71 @@ void vm_cache_remove_consumer(vm_cache_ref *cacheRef, vm_cache *consumer) { vm_cache *cache = cacheRef->cache; + vm_cache *newSource = NULL; + 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! + vm_cache_ref *consumerRef; + vm_page *page, *nextPage; + + consumer = list_get_first_item(&cache->consumers); + consumerRef = consumer->ref; + + mutex_lock(&consumerRef->lock); + // TODO: is it okay to lock them in this direction? + TRACE(("merge vm cache %p (ref == %ld) with vm cache %p\n", - cache, cacheRef->ref_count, cache->consumers.link.next)); + cache, cacheRef->ref_count, consumer)); + + for (page = cache->page_list; page != NULL; page = nextPage) { + nextPage = page->cache_next; + vm_cache_remove_page(cacheRef, page); + + if (vm_cache_lookup_page(consumerRef, + (off_t)page->cache_offset << PAGE_SHIFT) != NULL) { + // the page already is in the consumer cache - this copy is no + // longer needed, and can be freed + vm_page_set_state(page, PAGE_STATE_FREE); + continue; + } + + // move the page into the consumer cache + vm_cache_insert_page(consumerRef, page, + (off_t)page->cache_offset << PAGE_SHIFT); + } + + newSource = cache->source; + mutex_unlock(&consumerRef->lock); } mutex_unlock(&cacheRef->lock); + + if (newSource != NULL) { + // The remaining consumer has gotten a new source + mutex_lock(&cacheRef->lock); + + list_remove_item(&newSource->consumers, cache); + list_add_item(&newSource->consumers, consumer); + consumer->source = newSource; + cache->source = NULL; + + mutex_unlock(&cacheRef->lock); + + // Release the other reference to the cache - we take over + // its reference of its source cache + vm_cache_release_ref(cacheRef); + } + vm_cache_release_ref(cacheRef); }