diff --git a/headers/os/drivers/fs_cache.h b/headers/os/drivers/fs_cache.h index fd7cf4af3e..64667699a5 100644 --- a/headers/os/drivers/fs_cache.h +++ b/headers/os/drivers/fs_cache.h @@ -33,70 +33,72 @@ extern "C" { #endif /* transactions */ -extern int32 cache_start_transaction(void *_cache); -extern status_t cache_sync_transaction(void *_cache, int32 id); -extern status_t cache_end_transaction(void *_cache, int32 id, +extern int32 cache_start_transaction(void *cache); +extern status_t cache_sync_transaction(void *cache, int32 id); +extern status_t cache_end_transaction(void *cache, int32 id, transaction_notification_hook hook, void *data); -extern status_t cache_abort_transaction(void *_cache, int32 id); -extern int32 cache_detach_sub_transaction(void *_cache, int32 id, +extern status_t cache_abort_transaction(void *cache, int32 id); +extern int32 cache_detach_sub_transaction(void *cache, int32 id, transaction_notification_hook hook, void *data); -extern status_t cache_abort_sub_transaction(void *_cache, int32 id); -extern status_t cache_start_sub_transaction(void *_cache, int32 id); -extern status_t cache_add_transaction_listener(void *_cache, int32 id, +extern status_t cache_abort_sub_transaction(void *cache, int32 id); +extern status_t cache_start_sub_transaction(void *cache, int32 id); +extern status_t cache_add_transaction_listener(void *cache, int32 id, int32 events, transaction_notification_hook hook, void *data); -extern status_t cache_remove_transaction_listener(void *_cache, int32 id, +extern status_t cache_remove_transaction_listener(void *cache, int32 id, transaction_notification_hook hook, void *data); -extern status_t cache_next_block_in_transaction(void *_cache, int32 id, +extern status_t cache_next_block_in_transaction(void *cache, int32 id, bool mainOnly, long *_cookie, off_t *_blockNumber, void **_data, void **_unchangedData); -extern int32 cache_blocks_in_transaction(void *_cache, int32 id); -extern int32 cache_blocks_in_main_transaction(void *_cache, int32 id); -extern int32 cache_blocks_in_sub_transaction(void *_cache, int32 id); +extern int32 cache_blocks_in_transaction(void *cache, int32 id); +extern int32 cache_blocks_in_main_transaction(void *cache, int32 id); +extern int32 cache_blocks_in_sub_transaction(void *cache, int32 id); /* block cache */ -extern void block_cache_delete(void *_cache, bool allowWrites); +extern void block_cache_delete(void *cache, bool allowWrites); extern void *block_cache_create(int fd, off_t numBlocks, size_t blockSize, bool readOnly); -extern status_t block_cache_sync(void *_cache); -extern status_t block_cache_sync_etc(void *_cache, off_t blockNumber, +extern status_t block_cache_sync(void *cache); +extern status_t block_cache_sync_etc(void *cache, off_t blockNumber, size_t numBlocks); -extern status_t block_cache_make_writable(void *_cache, off_t blockNumber, +extern void block_cache_discard(void *cache, off_t blockNumber, + size_t numBlocks); +extern status_t block_cache_make_writable(void *cache, off_t blockNumber, int32 transaction); -extern void *block_cache_get_writable_etc(void *_cache, off_t blockNumber, +extern void *block_cache_get_writable_etc(void *cache, off_t blockNumber, off_t base, off_t length, int32 transaction); -extern void *block_cache_get_writable(void *_cache, off_t blockNumber, +extern void *block_cache_get_writable(void *cache, off_t blockNumber, int32 transaction); -extern void *block_cache_get_empty(void *_cache, off_t blockNumber, +extern void *block_cache_get_empty(void *cache, off_t blockNumber, int32 transaction); -extern const void *block_cache_get_etc(void *_cache, off_t blockNumber, +extern const void *block_cache_get_etc(void *cache, off_t blockNumber, off_t base, off_t length); -extern const void *block_cache_get(void *_cache, off_t blockNumber); -extern status_t block_cache_set_dirty(void *_cache, off_t blockNumber, +extern const void *block_cache_get(void *cache, off_t blockNumber); +extern status_t block_cache_set_dirty(void *cache, off_t blockNumber, bool isDirty, int32 transaction); -extern void block_cache_put(void *_cache, off_t blockNumber); +extern void block_cache_put(void *cache, off_t blockNumber); /* file cache */ extern void *file_cache_create(dev_t mountID, ino_t vnodeID, off_t size); -extern void file_cache_delete(void *_cacheRef); -extern void file_cache_enable(void *_cacheRef); -extern bool file_cache_is_enabled(void *_cacheRef); -extern status_t file_cache_disable(void *_cacheRef); -extern status_t file_cache_set_size(void *_cacheRef, off_t size); -extern status_t file_cache_sync(void *_cache); +extern void file_cache_delete(void *cacheRef); +extern void file_cache_enable(void *cacheRef); +extern bool file_cache_is_enabled(void *cacheRef); +extern status_t file_cache_disable(void *cacheRef); +extern status_t file_cache_set_size(void *cacheRef, off_t size); +extern status_t file_cache_sync(void *cache); -extern status_t file_cache_read(void *_cacheRef, void *cookie, off_t offset, +extern status_t file_cache_read(void *cacheRef, void *cookie, off_t offset, void *bufferBase, size_t *_size); -extern status_t file_cache_write(void *_cacheRef, void *cookie, off_t offset, +extern status_t file_cache_write(void *cacheRef, void *cookie, off_t offset, const void *buffer, size_t *_size); /* file map */ extern void *file_map_create(dev_t mountID, ino_t vnodeID, off_t size); -extern void file_map_delete(void *_map); -extern void file_map_set_size(void *_map, off_t size); -extern void file_map_invalidate(void *_map, off_t offset, off_t size); -extern status_t file_map_set_mode(void *_map, uint32 mode); -extern status_t file_map_translate(void *_map, off_t offset, size_t size, +extern void file_map_delete(void *map); +extern void file_map_set_size(void *map, off_t size); +extern void file_map_invalidate(void *map, off_t offset, off_t size); +extern status_t file_map_set_mode(void *map, uint32 mode); +extern status_t file_map_translate(void *map, off_t offset, size_t size, struct file_io_vec *vecs, size_t *_count, size_t align); /* entry cache */ diff --git a/headers/private/fs_shell/fssh_api_wrapper.h b/headers/private/fs_shell/fssh_api_wrapper.h index 58d5a389a8..1a4c7d3eef 100644 --- a/headers/private/fs_shell/fssh_api_wrapper.h +++ b/headers/private/fs_shell/fssh_api_wrapper.h @@ -820,6 +820,7 @@ #define block_cache_create fssh_block_cache_create #define block_cache_sync fssh_block_cache_sync #define block_cache_sync_etc fssh_block_cache_sync_etc +#define block_cache_discard fssh_block_cache_discard #define block_cache_make_writable fssh_block_cache_make_writable #define block_cache_get_writable_etc fssh_block_cache_get_writable_etc #define block_cache_get_writable fssh_block_cache_get_writable diff --git a/headers/private/fs_shell/fssh_fs_cache.h b/headers/private/fs_shell/fssh_fs_cache.h index 4873bd4075..87eef13f39 100644 --- a/headers/private/fs_shell/fssh_fs_cache.h +++ b/headers/private/fs_shell/fssh_fs_cache.h @@ -70,6 +70,8 @@ extern void * fssh_block_cache_create(int fd, fssh_off_t numBlocks, extern fssh_status_t fssh_block_cache_sync(void *_cache); extern fssh_status_t fssh_block_cache_sync_etc(void *_cache, fssh_off_t blockNumber, fssh_size_t numBlocks); +extern void fssh_block_cache_discard(void *_cache, + fssh_off_t blockNumber, fssh_size_t numBlocks); extern fssh_status_t fssh_block_cache_make_writable(void *_cache, fssh_off_t blockNumber, int32_t transaction); extern void * fssh_block_cache_get_writable_etc(void *_cache, diff --git a/src/system/kernel/cache/block_cache.cpp b/src/system/kernel/cache/block_cache.cpp index 18df3486e8..c9c01a97d4 100644 --- a/src/system/kernel/cache/block_cache.cpp +++ b/src/system/kernel/cache/block_cache.cpp @@ -74,6 +74,7 @@ struct cached_block { bool is_writing : 1; bool is_dirty : 1; bool unused : 1; + bool discard : 1; cache_transaction *transaction; cache_transaction *previous_transaction; @@ -122,12 +123,13 @@ struct block_cache : DoublyLinkedListLinkImpl { void RemoveUnusedBlocks(int32 maxAccessed = LONG_MAX, int32 count = LONG_MAX); - void FreeBlock(cached_block *block); - cached_block *NewBlock(off_t blockNumber); - void Free(void *buffer); - void *Allocate(); + void RemoveBlock(cached_block* block); + void FreeBlock(cached_block* block); + cached_block* NewBlock(off_t blockNumber); + void Free(void* buffer); + void* Allocate(); - static void LowMemoryHandler(void *data, uint32 resources, int32 level); + static void LowMemoryHandler(void* data, uint32 resources, int32 level); private: cached_block *_GetUnusedBlock(); @@ -909,6 +911,7 @@ block_cache::NewBlock(off_t blockNumber) block->parent_data = NULL; block->is_dirty = false; block->unused = false; + block->discard = false; #if BLOCK_CACHE_DEBUG_CHANGED block->compare = NULL; #endif @@ -917,6 +920,14 @@ block_cache::NewBlock(off_t blockNumber) } +void +block_cache::RemoveBlock(cached_block* block) +{ + hash_remove(hash, block); + FreeBlock(block); +} + + void block_cache::RemoveUnusedBlocks(int32 maxAccessed, int32 count) { @@ -932,14 +943,12 @@ block_cache::RemoveUnusedBlocks(int32 maxAccessed, int32 count) block->block_number, block->accessed)); // this can only happen if no transactions are used - if (block->is_dirty) + if (block->is_dirty && !block->discard) write_cached_block(this, block, false); // remove block from lists iterator.Remove(); - hash_remove(hash, block); - - FreeBlock(block); + RemoveBlock(block); if (--count <= 0) break; @@ -1022,17 +1031,20 @@ put_cached_block(block_cache *cache, cached_block *block) } if (--block->ref_count == 0 - && block->transaction == NULL - && block->previous_transaction == NULL) { - // put this block in the list of unused blocks - block->unused = true; -if (block->original_data != NULL || block->parent_data != NULL) - panic("put_cached_block(): %p (%Ld): %p, %p\n", block, block->block_number, block->original_data, block->parent_data); - cache->unused_blocks.Add(block); -// block->current_data = cache->allocator->Release(block->current_data); + && block->transaction == NULL && block->previous_transaction == NULL) { + // This block is not used anymore, and not part of any transaction + if (block->discard) { + cache->RemoveBlock(block); + } else { + // put this block in the list of unused blocks + block->unused = true; + ASSERT(block->original_data == NULL + && block->parent_data == NULL); + cache->unused_blocks.Add(block); + } } - // free some blocks according to the low memory state + // Free some blocks according to the low memory state // (if there is enough memory left, we don't free any) int32 free = 1; @@ -1111,8 +1123,7 @@ get_cached_block(block_cache *cache, off_t blockNumber, bool *_allocated, ssize_t bytesRead = read_pos(cache->fd, blockNumber * blockSize, block->current_data, blockSize); if (bytesRead < blockSize) { - hash_remove(cache->hash, block); - cache->FreeBlock(block); + cache->RemoveBlock(block); TB(Error(cache, blockNumber, "read failed", bytesRead)); FATAL(("could not read block %Ld: bytesRead: %ld, error: %s\n", @@ -1160,6 +1171,8 @@ get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, if (block == NULL) return NULL; + block->discard = false; + // if there is no transaction support, we just return the current block if (transactionID == -1) { if (cleared) @@ -1843,8 +1856,9 @@ cache_sync_transaction(void *_cache, int32 id) T(Action("sync", cache, transaction)); while (transaction->num_blocks > 0) { // sort blocks to speed up writing them back - // TODO: ideally, this should be handled by the I/O scheduler - block_list::Iterator iterator = transaction->blocks.GetIterator(); + // TODO: this should be handled by the I/O scheduler + block_list::Iterator iterator + = transaction->blocks.GetIterator(); uint32 maxCount = transaction->num_blocks; cached_block *buffer[16]; cached_block **blocks = (cached_block **)malloc(maxCount @@ -1984,6 +1998,7 @@ cache_abort_transaction(void *_cache, int32 id) block->transaction_next = NULL; block->transaction = NULL; + block->discard = false; } hash_remove(cache->transaction_hash, transaction); @@ -2251,10 +2266,16 @@ cache_next_block_in_transaction(void *_cache, int32 id, bool mainOnly, else block = block->transaction_next; - if (mainOnly && transaction->has_sub_transaction) { - // find next block that the parent changed - while (block != NULL && block->parent_data == NULL) - block = block->transaction_next; + if (transaction->has_sub_transaction) { + if (mainOnly) { + // find next block that the parent changed + while (block != NULL && block->parent_data == NULL) + block = block->transaction_next; + } else { + // find next non-discarded block + while (block != NULL && block->discard) + block = block->transaction_next; + } } if (block == NULL) @@ -2427,8 +2448,6 @@ block_cache_sync_etc(void *_cache, off_t blockNumber, size_t numBlocks) if (block == NULL) continue; - // TODO: sort blocks! - if (block->previous_transaction != NULL || (block->transaction == NULL && block->is_dirty)) { status_t status = write_cached_block(cache, block); @@ -2446,6 +2465,32 @@ block_cache_sync_etc(void *_cache, off_t blockNumber, size_t numBlocks) } +extern "C" void +block_cache_discard(void* _cache, off_t blockNumber, size_t numBlocks) +{ + block_cache* cache = (block_cache*)_cache; + MutexLocker locker(&cache->lock); + + for (; numBlocks > 0; numBlocks--, blockNumber++) { + cached_block* block = (cached_block*)hash_lookup(cache->hash, + &blockNumber); + if (block == NULL) + continue; + + if (block->unused) { + cache->unused_blocks.Remove(block); + cache->RemoveBlock(block); + } else { + // mark them as discarded (in the current transaction only, if any) + if (block->previous_transaction != NULL) + write_cached_block(cache, block); + + block->discard = true; + } + } +} + + extern "C" status_t block_cache_make_writable(void *_cache, off_t blockNumber, int32 transaction) { diff --git a/src/tests/system/kernel/cache/Jamfile b/src/tests/system/kernel/cache/Jamfile index d7a5912714..3a1f3a29d5 100644 --- a/src/tests/system/kernel/cache/Jamfile +++ b/src/tests/system/kernel/cache/Jamfile @@ -1,11 +1,10 @@ SubDir HAIKU_TOP src tests system kernel cache ; +UsePrivateSystemHeaders ; +UsePrivateKernelHeaders ; + SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src system kernel cache ] ; SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src system kernel util ] ; - -UsePrivateHeaders kernel ; -UsePrivateHeaders [ FDirName kernel arch $(TARGET_ARCH) ] ; -UsePrivateHeaders [ FDirName kernel boot platform $(TARGET_BOOT_PLATFORM) ] ; UseHeaders [ FDirName $(HAIKU_TOP) src system kernel cache ] ; StdBinCommands @@ -18,6 +17,10 @@ SimpleTest BlockMapTest : khash.c : libkernelland_emu.so ; +SimpleTest block_cache_test : + block_cache_test.cpp + : libkernelland_emu.so ; + SimpleTest pages_io_test : pages_io_test.cpp ; diff --git a/src/tests/system/kernel/cache/block_cache_test.cpp b/src/tests/system/kernel/cache/block_cache_test.cpp new file mode 100644 index 0000000000..b25784a4a6 --- /dev/null +++ b/src/tests/system/kernel/cache/block_cache_test.cpp @@ -0,0 +1,13 @@ +/* + * Copyright 2008, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + +#include "block_cache.cpp" + + +int +main(int argc, char** argv) +{ + return 0; +} diff --git a/src/tools/fs_shell/block_cache.cpp b/src/tools/fs_shell/block_cache.cpp index 047eb62cf4..8dd3b90be5 100644 --- a/src/tools/fs_shell/block_cache.cpp +++ b/src/tools/fs_shell/block_cache.cpp @@ -3,6 +3,7 @@ * Distributed under the terms of the MIT License. */ + #include #include @@ -73,7 +74,7 @@ struct cached_block { bool is_writing : 1; bool is_dirty : 1; bool unused : 1; - bool unmapped : 1; + bool discard : 1; cache_transaction *transaction; cache_transaction *previous_transaction; @@ -106,6 +107,7 @@ struct block_cache { fssh_status_t InitCheck(); void RemoveUnusedBlocks(int32_t maxAccessed = LONG_MAX, int32_t count = LONG_MAX); + void RemoveBlock(cached_block* block); void FreeBlock(cached_block *block); cached_block *NewBlock(fssh_off_t blockNumber); void Free(void *address); @@ -364,6 +366,7 @@ block_cache::NewBlock(fssh_off_t blockNumber) block->parent_data = NULL; block->is_dirty = false; block->unused = false; + block->discard = false; #ifdef DEBUG_CHANGED block->compare = NULL; #endif @@ -374,14 +377,21 @@ block_cache::NewBlock(fssh_off_t blockNumber) } +void +block_cache::RemoveBlock(cached_block* block) +{ + hash_remove(hash, block); + FreeBlock(block); +} + + void block_cache::RemoveUnusedBlocks(int32_t maxAccessed, int32_t count) { TRACE(("block_cache: remove up to %ld unused blocks\n", count)); - for (block_list::Iterator it = unused_blocks.GetIterator(); - cached_block *block = it.Next();) { - + for (block_list::Iterator iterator = unused_blocks.GetIterator(); + cached_block *block = iterator.Next();) { if (maxAccessed < block->accessed) continue; @@ -393,10 +403,8 @@ block_cache::RemoveUnusedBlocks(int32_t maxAccessed, int32_t count) write_cached_block(this, block, false); // remove block from lists - it.Remove(); - hash_remove(hash, block); - - FreeBlock(block); + iterator.Remove(); + RemoveBlock(block); if (--count <= 0) break; @@ -426,11 +434,15 @@ put_cached_block(block_cache *cache, cached_block *block) #endif if (--block->ref_count == 0 - && block->transaction == NULL - && block->previous_transaction == NULL) { - // put this block in the list of unused blocks - block->unused = true; - cache->unused_blocks.Add(block); + && block->transaction == NULL && block->previous_transaction == NULL) { + // This block is not used anymore, and not part of any transaction + if (block->discard) { + cache->RemoveBlock(block); + } else { + // put this block in the list of unused blocks + block->unused = true; + cache->unused_blocks.Add(block); + } } if (cache->allocated_block_count > kMaxBlockCount) { @@ -539,6 +551,8 @@ get_writable_cached_block(block_cache *cache, fssh_off_t blockNumber, fssh_off_t if (block == NULL) return NULL; + block->discard = false; + // if there is no transaction support, we just return the current block if (transactionID == -1) { if (cleared) @@ -1117,10 +1131,16 @@ fssh_cache_next_block_in_transaction(void *_cache, int32_t id, bool mainOnly, else block = block->transaction_next; - if (mainOnly && transaction->has_sub_transaction) { - // find next block that the parent changed - while (block != NULL && block->parent_data == NULL) - block = block->transaction_next; + if (transaction->has_sub_transaction) { + if (mainOnly) { + // find next block that the parent changed + while (block != NULL && block->parent_data == NULL) + block = block->transaction_next; + } else { + // find next non-discarded block + while (block != NULL && block->discard) + block = block->transaction_next; + } } if (block == NULL) @@ -1282,6 +1302,7 @@ fssh_block_cache_sync_etc(void *_cache, fssh_off_t blockNumber, &blockNumber); if (block == NULL) continue; + if (block->previous_transaction != NULL || (block->transaction == NULL && block->is_dirty)) { fssh_status_t status = write_cached_block(cache, block); @@ -1294,8 +1315,36 @@ fssh_block_cache_sync_etc(void *_cache, fssh_off_t blockNumber, } +void +fssh_block_cache_discard(void* _cache, fssh_off_t blockNumber, + fssh_size_t numBlocks) +{ + block_cache* cache = (block_cache*)_cache; + MutexLocker locker(&cache->lock); + + for (; numBlocks > 0; numBlocks--, blockNumber++) { + cached_block* block = (cached_block*)hash_lookup(cache->hash, + &blockNumber); + if (block == NULL) + continue; + + if (block->unused) { + cache->unused_blocks.Remove(block); + cache->RemoveBlock(block); + } else { + // mark them as discarded (in the current transaction only, if any) + if (block->previous_transaction != NULL) + write_cached_block(cache, block); + + block->discard = true; + } + } +} + + fssh_status_t -fssh_block_cache_make_writable(void *_cache, fssh_off_t blockNumber, int32_t transaction) +fssh_block_cache_make_writable(void *_cache, fssh_off_t blockNumber, + int32_t transaction) { block_cache *cache = (block_cache *)_cache; MutexLocker locker(&cache->lock);