* Added function block_cache_discard() that flushes blocks from the block cache,

discarding their changes. This functionality currently only works correctly
  when no transactions are used.
* Started test application for the block cache, doesn't do anything yet.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@28496 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2008-11-04 14:49:33 +00:00
parent 5ae0bbaad0
commit 5b812019b4
7 changed files with 202 additions and 87 deletions

View File

@ -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 */

View File

@ -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

View File

@ -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,

View File

@ -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,6 +123,7 @@ struct block_cache : DoublyLinkedListLinkImpl<block_cache> {
void RemoveUnusedBlocks(int32 maxAccessed = LONG_MAX,
int32 count = LONG_MAX);
void RemoveBlock(cached_block* block);
void FreeBlock(cached_block* block);
cached_block* NewBlock(off_t blockNumber);
void Free(void* buffer);
@ -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) {
&& 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;
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);
ASSERT(block->original_data == NULL
&& block->parent_data == NULL);
cache->unused_blocks.Add(block);
// block->current_data = cache->allocator->Release(block->current_data);
}
}
// 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) {
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)
{

View File

@ -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
;

View File

@ -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;
}

View File

@ -3,6 +3,7 @@
* Distributed under the terms of the MIT License.
*/
#include <new>
#include <stdlib.h>
@ -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,12 +434,16 @@ put_cached_block(block_cache *cache, cached_block *block)
#endif
if (--block->ref_count == 0
&& block->transaction == NULL
&& block->previous_transaction == NULL) {
&& 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) {
cache->RemoveUnusedBlocks(LONG_MAX,
@ -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) {
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);