* Broke the cache merging code out of vm_cache_remove_consumer() into
new function merge_cache_with_only_consumer(), which is also used in vm_cache_remove_area(), now. * Added tracing for the merge case. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25716 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
0a871009db
commit
ad66253c1a
@ -193,6 +193,27 @@ class RemoveConsumer : public VMCacheTraceEntry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Merge : public VMCacheTraceEntry {
|
||||||
|
public:
|
||||||
|
Merge(vm_cache* cache, vm_cache* consumer)
|
||||||
|
:
|
||||||
|
VMCacheTraceEntry(cache),
|
||||||
|
fConsumer(consumer)
|
||||||
|
{
|
||||||
|
Initialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void AddDump(TraceOutput& out)
|
||||||
|
{
|
||||||
|
out.Print("vm cache merge with consumer: cache: %p, consumer: %p",
|
||||||
|
fCache, fConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
vm_cache* fConsumer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class InsertArea : public VMCacheTraceEntry {
|
class InsertArea : public VMCacheTraceEntry {
|
||||||
public:
|
public:
|
||||||
InsertArea(vm_cache* cache, vm_area* area)
|
InsertArea(vm_cache* cache, vm_area* area)
|
||||||
@ -359,6 +380,140 @@ delete_cache(vm_cache* cache)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_cache_mergeable(vm_cache* cache)
|
||||||
|
{
|
||||||
|
return (cache->areas == NULL && cache->source != NULL
|
||||||
|
&& !list_is_empty(&cache->consumers)
|
||||||
|
&& cache->consumers.link.next == cache->consumers.link.prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Merges the given cache with its only consumer.
|
||||||
|
The caller must own a reference (other than the one from the consumer) and
|
||||||
|
hold the cache's lock.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
merge_cache_with_only_consumer(vm_cache* cache)
|
||||||
|
{
|
||||||
|
vm_cache* consumer = (vm_cache*)list_get_first_item(&cache->consumers);
|
||||||
|
|
||||||
|
if (!acquire_unreferenced_cache_ref(consumer))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// In case we managed to grab a reference to the consumerRef,
|
||||||
|
// this doesn't guarantee that we get the cache we wanted
|
||||||
|
// to, so we need to check if this cache is really the last
|
||||||
|
// consumer of the cache we want to merge it with.
|
||||||
|
|
||||||
|
ConditionVariable busyCondition;
|
||||||
|
|
||||||
|
// But since we need to keep the locking order upper->lower cache, we
|
||||||
|
// need to unlock our cache now
|
||||||
|
busyCondition.Publish(cache, "cache");
|
||||||
|
cache->busy = true;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
mutex_unlock(&cache->lock);
|
||||||
|
|
||||||
|
mutex_lock(&consumer->lock);
|
||||||
|
mutex_lock(&cache->lock);
|
||||||
|
|
||||||
|
bool isMergeable = is_cache_mergeable(cache);
|
||||||
|
if (isMergeable && consumer == list_get_first_item(&cache->consumers))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// something changed, get rid of the consumer lock and ref
|
||||||
|
mutex_unlock(&consumer->lock);
|
||||||
|
vm_cache_release_ref(consumer);
|
||||||
|
|
||||||
|
if (!isMergeable) {
|
||||||
|
dprintf("merge_cache_with_only_consumer(): cache %p is no longer; "
|
||||||
|
"mergeable\n", cache);
|
||||||
|
cache->busy = false;
|
||||||
|
busyCondition.Unpublish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the cache is still mergeable, just the consumer changed
|
||||||
|
consumer = (vm_cache*)list_get_first_item(&cache->consumers);
|
||||||
|
|
||||||
|
if (!acquire_unreferenced_cache_ref(consumer))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
consumer = (vm_cache*)list_remove_head_item(&cache->consumers);
|
||||||
|
|
||||||
|
TRACE(("merge vm cache %p (ref == %ld) with vm cache %p\n",
|
||||||
|
cache, cache->ref_count, consumer));
|
||||||
|
|
||||||
|
T(Merge(cache, consumer));
|
||||||
|
|
||||||
|
vm_page* nextPage;
|
||||||
|
for (vm_page* page = cache->page_list; page != NULL;
|
||||||
|
page = nextPage) {
|
||||||
|
vm_page* consumerPage;
|
||||||
|
nextPage = page->cache_next;
|
||||||
|
|
||||||
|
consumerPage = vm_cache_lookup_page(consumer,
|
||||||
|
(off_t)page->cache_offset << PAGE_SHIFT);
|
||||||
|
if (consumerPage == NULL) {
|
||||||
|
// the page is not yet in the consumer cache - move it upwards
|
||||||
|
vm_cache_remove_page(cache, page);
|
||||||
|
vm_cache_insert_page(consumer, page,
|
||||||
|
(off_t)page->cache_offset << PAGE_SHIFT);
|
||||||
|
} else if (consumerPage->state == PAGE_STATE_BUSY
|
||||||
|
&& consumerPage->type == PAGE_TYPE_DUMMY) {
|
||||||
|
// the page is currently busy taking a read fault - IOW,
|
||||||
|
// vm_soft_fault() has mapped our page so we can just
|
||||||
|
// move it up
|
||||||
|
//dprintf("%ld: merged busy page %p, cache %p, offset %ld\n", find_thread(NULL), page, cacheRef->cache, page->cache_offset);
|
||||||
|
vm_cache_remove_page(consumer, consumerPage);
|
||||||
|
consumerPage->state = PAGE_STATE_INACTIVE;
|
||||||
|
((vm_dummy_page*)consumerPage)->busy_condition.Unpublish();
|
||||||
|
|
||||||
|
vm_cache_remove_page(cache, page);
|
||||||
|
vm_cache_insert_page(consumer, page,
|
||||||
|
(off_t)page->cache_offset << PAGE_SHIFT);
|
||||||
|
#ifdef DEBUG_PAGE_CACHE_TRANSITIONS
|
||||||
|
} else {
|
||||||
|
page->debug_flags = 0;
|
||||||
|
if (consumerPage->state == PAGE_STATE_BUSY)
|
||||||
|
page->debug_flags |= 0x1;
|
||||||
|
if (consumerPage->type == PAGE_TYPE_DUMMY)
|
||||||
|
page->debug_flags |= 0x2;
|
||||||
|
page->collided_page = consumerPage;
|
||||||
|
consumerPage->collided_page = page;
|
||||||
|
#endif // DEBUG_PAGE_CACHE_TRANSITIONS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_cache* newSource = cache->source;
|
||||||
|
|
||||||
|
// The remaining consumer has got a new source
|
||||||
|
mutex_lock(&newSource->lock);
|
||||||
|
|
||||||
|
list_remove_item(&newSource->consumers, cache);
|
||||||
|
list_add_item(&newSource->consumers, consumer);
|
||||||
|
consumer->source = newSource;
|
||||||
|
cache->source = NULL;
|
||||||
|
|
||||||
|
mutex_unlock(&newSource->lock);
|
||||||
|
|
||||||
|
// Release the reference the cache's consumer owned. The consumer takes
|
||||||
|
// over the cache's ref to its source instead.
|
||||||
|
if (cache->ref_count < 2)
|
||||||
|
panic("cacheRef %p ref count too low!\n", cache);
|
||||||
|
vm_cache_release_ref(cache);
|
||||||
|
|
||||||
|
cache->busy = false;
|
||||||
|
busyCondition.Unpublish();
|
||||||
|
|
||||||
|
mutex_unlock(&consumer->lock);
|
||||||
|
vm_cache_release_ref(consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@ -750,120 +905,9 @@ vm_cache_remove_consumer(vm_cache* cache, vm_cache* consumer)
|
|||||||
list_remove_item(&cache->consumers, consumer);
|
list_remove_item(&cache->consumers, consumer);
|
||||||
consumer->source = NULL;
|
consumer->source = NULL;
|
||||||
|
|
||||||
if (cache->areas == NULL && cache->source != NULL
|
// merge cache, if possible
|
||||||
&& !list_is_empty(&cache->consumers)
|
if (is_cache_mergeable(cache))
|
||||||
&& cache->consumers.link.next == cache->consumers.link.prev) {
|
merge_cache_with_only_consumer(cache);
|
||||||
// The cache is not really needed anymore - it can be merged with its only
|
|
||||||
// consumer left.
|
|
||||||
|
|
||||||
consumer = (vm_cache*)list_get_first_item(&cache->consumers);
|
|
||||||
|
|
||||||
bool merge = acquire_unreferenced_cache_ref(consumer);
|
|
||||||
|
|
||||||
// In case we managed to grab a reference to the consumerRef,
|
|
||||||
// this doesn't guarantee that we get the cache we wanted
|
|
||||||
// to, so we need to check if this cache is really the last
|
|
||||||
// consumer of the cache we want to merge it with.
|
|
||||||
|
|
||||||
ConditionVariable busyCondition;
|
|
||||||
|
|
||||||
if (merge) {
|
|
||||||
// But since we need to keep the locking order upper->lower cache, we
|
|
||||||
// need to unlock our cache now
|
|
||||||
busyCondition.Publish(cache, "cache");
|
|
||||||
cache->busy = true;
|
|
||||||
mutex_unlock(&cache->lock);
|
|
||||||
|
|
||||||
mutex_lock(&consumer->lock);
|
|
||||||
mutex_lock(&cache->lock);
|
|
||||||
|
|
||||||
if (cache->areas != NULL || cache->source == NULL
|
|
||||||
|| list_is_empty(&cache->consumers)
|
|
||||||
|| cache->consumers.link.next != cache->consumers.link.prev
|
|
||||||
|| consumer != list_get_first_item(&cache->consumers)) {
|
|
||||||
dprintf("vm_cache_remove_consumer(): cache %p was modified; "
|
|
||||||
"not merging it\n", cache);
|
|
||||||
merge = false;
|
|
||||||
cache->busy = false;
|
|
||||||
busyCondition.Unpublish();
|
|
||||||
mutex_unlock(&consumer->lock);
|
|
||||||
vm_cache_release_ref(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (merge) {
|
|
||||||
consumer = (vm_cache*)list_remove_head_item(&cache->consumers);
|
|
||||||
|
|
||||||
TRACE(("merge vm cache %p (ref == %ld) with vm cache %p\n",
|
|
||||||
cache, cache->ref_count, consumer));
|
|
||||||
|
|
||||||
vm_page* nextPage;
|
|
||||||
for (vm_page* page = cache->page_list; page != NULL;
|
|
||||||
page = nextPage) {
|
|
||||||
vm_page* consumerPage;
|
|
||||||
nextPage = page->cache_next;
|
|
||||||
|
|
||||||
consumerPage = vm_cache_lookup_page(consumer,
|
|
||||||
(off_t)page->cache_offset << PAGE_SHIFT);
|
|
||||||
if (consumerPage == NULL) {
|
|
||||||
// the page already is not yet in the consumer cache - move
|
|
||||||
// it upwards
|
|
||||||
vm_cache_remove_page(cache, page);
|
|
||||||
vm_cache_insert_page(consumer, page,
|
|
||||||
(off_t)page->cache_offset << PAGE_SHIFT);
|
|
||||||
} else if (consumerPage->state == PAGE_STATE_BUSY
|
|
||||||
&& consumerPage->type == PAGE_TYPE_DUMMY) {
|
|
||||||
// the page is currently busy taking a read fault - IOW,
|
|
||||||
// vm_soft_fault() has mapped our page so we can just
|
|
||||||
// move it up
|
|
||||||
//dprintf("%ld: merged busy page %p, cache %p, offset %ld\n", find_thread(NULL), page, cacheRef->cache, page->cache_offset);
|
|
||||||
vm_cache_remove_page(consumer, consumerPage);
|
|
||||||
consumerPage->state = PAGE_STATE_INACTIVE;
|
|
||||||
((vm_dummy_page*)consumerPage)->busy_condition.Unpublish();
|
|
||||||
|
|
||||||
vm_cache_remove_page(cache, page);
|
|
||||||
vm_cache_insert_page(consumer, page,
|
|
||||||
(off_t)page->cache_offset << PAGE_SHIFT);
|
|
||||||
#ifdef DEBUG_PAGE_CACHE_TRANSITIONS
|
|
||||||
} else {
|
|
||||||
page->debug_flags = 0;
|
|
||||||
if (consumerPage->state == PAGE_STATE_BUSY)
|
|
||||||
page->debug_flags |= 0x1;
|
|
||||||
if (consumerPage->type == PAGE_TYPE_DUMMY)
|
|
||||||
page->debug_flags |= 0x2;
|
|
||||||
page->collided_page = consumerPage;
|
|
||||||
consumerPage->collided_page = page;
|
|
||||||
#endif // DEBUG_PAGE_CACHE_TRANSITIONS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
vm_cache* newSource = cache->source;
|
|
||||||
|
|
||||||
// The remaining consumer has gotten a new source
|
|
||||||
mutex_lock(&newSource->lock);
|
|
||||||
|
|
||||||
list_remove_item(&newSource->consumers, cache);
|
|
||||||
list_add_item(&newSource->consumers, consumer);
|
|
||||||
consumer->source = newSource;
|
|
||||||
cache->source = NULL;
|
|
||||||
|
|
||||||
mutex_unlock(&newSource->lock);
|
|
||||||
|
|
||||||
// Release the other reference to the cache - we take over
|
|
||||||
// its reference of its source cache; we can do this here
|
|
||||||
// (with the cacheRef locked) since we own another reference
|
|
||||||
// from the first consumer we removed
|
|
||||||
if (cache->ref_count < 2)
|
|
||||||
panic("cacheRef %p ref count too low!\n", cache);
|
|
||||||
vm_cache_release_ref(cache);
|
|
||||||
|
|
||||||
mutex_unlock(&consumer->lock);
|
|
||||||
vm_cache_release_ref(consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cache->busy)
|
|
||||||
busyCondition.Unpublish();
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&cache->lock);
|
mutex_unlock(&cache->lock);
|
||||||
vm_cache_release_ref(cache);
|
vm_cache_release_ref(cache);
|
||||||
@ -937,5 +981,9 @@ vm_cache_remove_area(vm_cache* cache, vm_area* area)
|
|||||||
if (cache->store->ops->release_ref)
|
if (cache->store->ops->release_ref)
|
||||||
cache->store->ops->release_ref(cache->store);
|
cache->store->ops->release_ref(cache->store);
|
||||||
|
|
||||||
|
// merge cache, if possible
|
||||||
|
if (is_cache_mergeable(cache))
|
||||||
|
merge_cache_with_only_consumer(cache);
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user