Author: axeld Date: 2010-03-03 11:28:57 +0100 (Wed, 03 Mar 2010) New Revision: 35733 Changeset: http://dev.haiku-os.org/changeset/35733/haiku Modified: haiku/trunk/src/system/kernel/cache/block_cache.cpp Log: * Replaced cached_block::accessed (the number of accesses) with last_accessed (the time of the last access), as what we really want is a frequency/last access time scoring, and accessed alone is useless for that. * put_cached_block() no longer frees any unused blocks. * The low memory handler will now only lock the cache if there is something to do. Also, it did not take address space warnings into account. * Even when memory is critical, we don't free all unused blocks anymore - if the number of blocks we free now (10000) is not sufficient to get out of the critical condition, chances are good that we will be called again :-) * block_notifier_and_writer() now tries to make sure that the total block cache memory consumption grows not much larger than half of the available RAM. * This should all help to limit the block cache usage a bit better. Hopefully, a checkfs run will no longer run out of memory here (couldn't test yet). Modified: haiku/trunk/src/system/kernel/cache/block_cache.cpp =================================================================== --- haiku/trunk/src/system/kernel/cache/block_cache.cpp 2010-03-03 07:59:53 UTC (rev 35732) +++ haiku/trunk/src/system/kernel/cache/block_cache.cpp 2010-03-03 10:28:57 UTC (rev 35733) @@ -24,6 +24,7 @@ #include <util/DoublyLinkedList.h> #include <util/AutoLock.h> #include <util/khash.h> +#include <vm/vm_page.h> #include "kernel_debug_config.h" @@ -66,7 +67,7 @@ void* compare; #endif int32 ref_count; - int32 accessed; + int32 last_accessed; bool busy_reading : 1; bool busy_writing : 1; bool is_writing : 1; @@ -81,6 +82,8 @@ cache_transaction* previous_transaction; bool CanBeWritten() const; + int32 LastAccess() const + { return system_time() / 1000000L - last_accessed; } static int Compare(void* _cacheEntry, const void* _block); static uint32 Hash(void* _cacheEntry, const void* _block, uint32 range); @@ -136,8 +139,7 @@ void Free(void* buffer); void* Allocate(); - void RemoveUnusedBlocks(int32 maxAccessed = LONG_MAX, - int32 count = LONG_MAX); + void RemoveUnusedBlocks(int32 count, int32 minSecondsOld = 0); void RemoveBlock(cached_block* block); void DiscardBlock(cached_block* block); void FreeBlock(cached_block* block); @@ -1365,7 +1367,7 @@ if (low_resource_state(B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY | B_KERNEL_RESOURCE_ADDRESS_SPACE) != B_NO_LOW_RESOURCE) { // recycle existing before allocating a new one - RemoveUnusedBlocks(1, 2); + RemoveUnusedBlocks(2); } void* block = object_cache_alloc(buffer_cache, 0); @@ -1373,7 +1375,7 @@ return block; // recycle existing before allocating a new one - RemoveUnusedBlocks(1, 2); + RemoveUnusedBlocks(100); return object_cache_alloc(buffer_cache, 0); } @@ -1433,7 +1435,7 @@ block->block_number = blockNumber; block->ref_count = 0; - block->accessed = 0; + block->last_accessed = 0; block->transaction_next = NULL; block->transaction = block->previous_transaction = NULL; block->original_data = NULL; @@ -1455,18 +1457,22 @@ void -block_cache::RemoveUnusedBlocks(int32 maxAccessed, int32 count) +block_cache::RemoveUnusedBlocks(int32 count, int32 minSecondsOld) { - TRACE(("block_cache: remove up to %ld unused blocks\n", count)); + TRACE(("block_cache: remove up to %" B_PRId32 " unused blocks\n", count)); for (block_list::Iterator iterator = unused_blocks.GetIterator(); cached_block* block = iterator.Next();) { - if (maxAccessed < block->accessed || block->busy_reading) + if (minSecondsOld >= block->LastAccess()) { + // The list is sorted by last access + break; + } + if (block->busy_reading || block->busy_writing) continue; TB(Flush(this, block)); - TRACE((" remove block %Ld, accessed %ld times\n", - block->block_number, block->accessed)); + TRACE((" remove block %Ld, last accessed %" B_PRId32 "\n", + block->block_number, block->last_accessed)); // this can only happen if no transactions are used if (block->is_dirty && !block->discard) { @@ -1519,42 +1525,41 @@ void block_cache::_LowMemoryHandler(void* data, uint32 resources, int32 level) { - block_cache* cache = (block_cache*)data; - MutexLocker locker(&cache->lock); - - if (!locker.IsLocked()) { - // If our block_cache were deleted, it could be that we had - // been called before that deletion went through, therefore, - // acquiring its lock might fail. - return; - } - TRACE(("block_cache: low memory handler called with level %ld\n", level)); // free some blocks according to the low memory state // (if there is enough memory left, we don't free any) - int32 free = 1; - int32 accessed = 1; - switch (low_resource_state( - B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY)) { + int32 free = 0; + int32 secondsOld = 0; + switch (level) { case B_NO_LOW_RESOURCE: return; case B_LOW_RESOURCE_NOTE: free = 50; - accessed = 2; + secondsOld = 120; break; case B_LOW_RESOURCE_WARNING: free = 200; - accessed = 10; + secondsOld = 10; break; case B_LOW_RESOURCE_CRITICAL: - free = LONG_MAX; - accessed = LONG_MAX; + free = 10000; + secondsOld = 0; break; } - cache->RemoveUnusedBlocks(accessed, free); + block_cache* cache = (block_cache*)data; + MutexLocker locker(&cache->lock); + + if (!locker.IsLocked()) { + // If our block_cache were deleted, it could be that we had + // been called before that deletion went through, therefore, + // acquiring its lock might fail. + return; + } + + cache->RemoveUnusedBlocks(free, secondsOld); } @@ -1742,27 +1747,6 @@ cache->unused_blocks.Add(block); } } - - // Free some blocks according to the low memory state - // (if there is enough memory left, we don't free any) - - int32 free = 1; - switch (low_resource_state( - B_KERNEL_RESOURCE_PAGES | B_KERNEL_RESOURCE_MEMORY)) { - case B_NO_LOW_RESOURCE: - return; - case B_LOW_RESOURCE_NOTE: - free = 1; - break; - case B_LOW_RESOURCE_WARNING: - free = 5; - break; - case B_LOW_RESOURCE_CRITICAL: - free = 20; - break; - } - - cache->RemoveUnusedBlocks(LONG_MAX, free); } @@ -1856,7 +1840,7 @@ } block->ref_count++; - block->accessed++; + block->last_accessed = system_time() / 1000000L; return block; } @@ -2022,7 +2006,7 @@ kprintf("%08lx %9Ld %08lx %08lx %08lx %5ld %6ld %c%c%c%c%c%c %08lx %08lx\n", (addr_t)block, block->block_number, (addr_t)block->current_data, (addr_t)block->original_data, - (addr_t)block->parent_data, block->ref_count, block->accessed, + (addr_t)block->parent_data, block->ref_count, block->LastAccess(), block->busy_reading ? 'r' : '-', block->busy_writing ? 'w' : '-', block->is_writing ? 'W' : '-', block->is_dirty ? 'D' : '-', block->unused ? 'U' : '-', block->discard ? 'D' : '-', @@ -2039,7 +2023,7 @@ kprintf(" original data: %p\n", block->original_data); kprintf(" parent data: %p\n", block->parent_data); kprintf(" ref_count: %ld\n", block->ref_count); - kprintf(" accessed: %ld\n", block->accessed); + kprintf(" accessed: %ld\n", block->LastAccess()); kprintf(" flags: "); if (block->busy_reading) kprintf(" busy_reading"); @@ -2514,6 +2498,13 @@ } writer.Write(); + + if ((block_cache_used_memory() / B_PAGE_SIZE) + > vm_page_num_pages() / 2) { + // Try to reduce memory usage to half of the available + // RAM at maximum + cache->RemoveUnusedBlocks(500, 10); + } } MutexLocker _(sCachesMemoryUseLock);