diff --git a/src/system/kernel/cache/block_allocator.cpp b/src/system/kernel/cache/block_allocator.cpp index 66862b0f39..3cba6209d1 100644 --- a/src/system/kernel/cache/block_allocator.cpp +++ b/src/system/kernel/cache/block_allocator.cpp @@ -212,6 +212,8 @@ block_range::New(block_cache *cache, block_range **_range) void block_range::Delete(block_cache *cache, block_range *range) { + TRACE(("delete block range %p, base = %p!\n", range, (void *)range->base)); + // unmap the memory vm_address_space *addressSpace = vm_get_kernel_aspace(); diff --git a/src/system/kernel/cache/block_cache.cpp b/src/system/kernel/cache/block_cache.cpp index b8cb742a17..c77d641a3f 100644 --- a/src/system/kernel/cache/block_cache.cpp +++ b/src/system/kernel/cache/block_cache.cpp @@ -175,11 +175,15 @@ block_cache::block_cache(int _fd, off_t numBlocks, size_t blockSize) chunks_per_range = kBlockRangeSize / chunk_size; range_mask = (1UL << chunks_per_range) - 1; chunk_mask = (1UL << (chunk_size / blockSize)) - 1; + + register_low_memory_handler(&block_cache::LowMemoryHandler, this, 0); } block_cache::~block_cache() { + unregister_low_memory_handler(&block_cache::LowMemoryHandler, this); + benaphore_destroy(&lock); hash_uninit(ranges_hash); @@ -291,6 +295,7 @@ block_cache::NewBlock(off_t blockNumber) block->block_number = blockNumber; block->ref_count = 0; + block->accessed = 0; block->transaction_next = NULL; block->transaction = block->previous_transaction = NULL; block->original = NULL; @@ -308,22 +313,71 @@ block_cache::NewBlock(off_t blockNumber) void -block_cache::RemoveUnusedBlocks(int32 count) +block_cache::RemoveUnusedBlocks(int32 maxAccessed, int32 count) { - while (count-- >= 0) { - cached_block *block = unused_blocks.First(); + dprintf("block_cache: remove up to %ld unused blocks\n", count); + + cached_block *next = NULL; + for (cached_block *block = unused_blocks.First(); block != NULL; block = next) { + next = block->next; + if (block == NULL) break; + if (maxAccessed < block->accessed) + continue; + + dprintf(" remove block %Ld, accessed %ld times\n", + block->block_number, block->accessed); // remove block from lists unused_blocks.Remove(block); hash_remove(hash, block); FreeBlock(block); + + if (--count <= 0) + break; } } +void +block_cache::LowMemoryHandler(void *data, int32 level) +{ + block_cache *cache = (block_cache *)data; + BenaphoreLocker locker(&cache->lock); + + dprintf("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 (vm_low_memory_state()) { + case B_NO_LOW_MEMORY: + return; + case B_LOW_MEMORY_NOTE: + free = 10; + accessed = 2; + break; + case B_LOW_MEMORY_WARNING: + free = 50; + accessed = 10; + break; + case B_LOW_MEMORY_CRITICAL: + free = LONG_MAX; + accessed = LONG_MAX; + break; + } + + cache->RemoveUnusedBlocks(accessed, free); +} + + +// #pragma mark - + + #ifdef DEBUG_CHANGED #define DUMPED_BLOCK_SIZE 16 @@ -409,7 +463,7 @@ put_cached_block(block_cache *cache, cached_block *block) break; } - cache->RemoveUnusedBlocks(free); + cache->RemoveUnusedBlocks(LONG_MAX, free); } @@ -469,6 +523,8 @@ get_cached_block(block_cache *cache, off_t blockNumber, bool &allocated, bool re } block->ref_count++; + block->accessed++; + return block; } diff --git a/src/system/kernel/cache/block_cache_private.h b/src/system/kernel/cache/block_cache_private.h index 2ef8e2c44d..f1840314fa 100644 --- a/src/system/kernel/cache/block_cache_private.h +++ b/src/system/kernel/cache/block_cache_private.h @@ -42,6 +42,7 @@ struct cached_block { void *compare; #endif int32 ref_count; + int32 accessed; bool busy : 1; bool is_writing : 1; bool is_dirty : 1; @@ -89,8 +90,6 @@ struct block_range { static int Compare(void *_blockRange, const void *_address); static uint32 Hash(void *_blockRange, const void *_address, uint32 range); - - //bool HasFreeBlocks() const { return (1L << num_chunks) - 1 != used_mask; } }; struct block_cache { @@ -119,11 +118,13 @@ struct block_cache { block_range *GetFreeRange(); block_range *GetRange(void *address); - void RemoveUnusedBlocks(int32 count = LONG_MAX); + void RemoveUnusedBlocks(int32 maxAccessed = LONG_MAX, int32 count = LONG_MAX); void FreeBlock(cached_block *block); cached_block *NewBlock(off_t blockNumber); void Free(void *address); void *Allocate(); + + static void LowMemoryHandler(void *data, int32 level); }; diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index f30807342b..a7444b6c5b 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -2753,6 +2753,8 @@ vm_try_reserve_memory(size_t amount) status_t status; benaphore_lock(&sAvailableMemoryLock); + //dprintf("try to reserve %lu bytes, %Lu left\n", amount, sAvailableMemory); + if (sAvailableMemory > amount) { sAvailableMemory -= amount; status = B_OK;