Cleanup:
* NewBlock()/FreeBlock() are now symmetrical in that the former no longer inserts the block into the hash table. * delete_transaction() also no longer removes the transaction from the hash table. * cache_transaction_sync() now uses the new hash_remove_current() function. * minor other cleanup (like line breaks). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20374 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
1f78f00899
commit
de4145dbb6
195
src/system/kernel/cache/block_cache.cpp
vendored
195
src/system/kernel/cache/block_cache.cpp
vendored
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@ -102,7 +102,6 @@ transaction_hash(void *_transaction, const void *_id, uint32 range)
|
||||
static void
|
||||
delete_transaction(block_cache *cache, cache_transaction *transaction)
|
||||
{
|
||||
hash_remove(cache->transaction_hash, transaction);
|
||||
if (cache->last_transaction == transaction)
|
||||
cache->last_transaction = NULL;
|
||||
|
||||
@ -149,7 +148,8 @@ cached_block::Hash(void *_cacheEntry, const void *_block, uint32 range)
|
||||
// #pragma mark - block_cache
|
||||
|
||||
|
||||
block_cache::block_cache(int _fd, off_t numBlocks, size_t blockSize, bool readOnly)
|
||||
block_cache::block_cache(int _fd, off_t numBlocks, size_t blockSize,
|
||||
bool readOnly)
|
||||
:
|
||||
hash(NULL),
|
||||
fd(_fd),
|
||||
@ -165,7 +165,8 @@ block_cache::block_cache(int _fd, off_t numBlocks, size_t blockSize, bool readOn
|
||||
if (hash == NULL)
|
||||
return;
|
||||
|
||||
transaction_hash = hash_init(16, 0, &transaction_compare, &::transaction_hash);
|
||||
transaction_hash = hash_init(16, 0, &transaction_compare,
|
||||
&::transaction_hash);
|
||||
if (transaction_hash == NULL)
|
||||
return;
|
||||
|
||||
@ -281,8 +282,10 @@ block_cache::FreeBlock(cached_block *block)
|
||||
ASSERT(range != NULL);
|
||||
range->Free(this, block);
|
||||
|
||||
if (block->original_data != NULL || block->parent_data != NULL)
|
||||
panic("block_cache::FreeBlock(): %p, %p\n", block->original_data, block->parent_data);
|
||||
if (block->original_data != NULL || block->parent_data != NULL) {
|
||||
panic("block_cache::FreeBlock(): %p, %p\n", block->original_data,
|
||||
block->parent_data);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CHANGED
|
||||
Free(block->compare);
|
||||
@ -295,6 +298,7 @@ block_cache::FreeBlock(cached_block *block)
|
||||
}
|
||||
|
||||
|
||||
/*! Allocates a new block for \a blockNumber, ready for use */
|
||||
cached_block *
|
||||
block_cache::NewBlock(off_t blockNumber)
|
||||
{
|
||||
@ -326,8 +330,6 @@ block_cache::NewBlock(off_t blockNumber)
|
||||
block->compare = NULL;
|
||||
#endif
|
||||
|
||||
hash_insert(hash, block);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
@ -338,7 +340,8 @@ block_cache::RemoveUnusedBlocks(int32 maxAccessed, int32 count)
|
||||
TRACE(("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) {
|
||||
for (cached_block *block = unused_blocks.First(); block != NULL;
|
||||
block = next) {
|
||||
next = block->next;
|
||||
|
||||
if (maxAccessed < block->accessed)
|
||||
@ -404,14 +407,15 @@ block_cache::LowMemoryHandler(void *data, int32 level)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// #pragma mark - private block functions
|
||||
|
||||
|
||||
static void
|
||||
put_cached_block(block_cache *cache, cached_block *block)
|
||||
{
|
||||
#ifdef DEBUG_CHANGED
|
||||
if (!block->is_dirty && block->compare != NULL && memcmp(block->current_data, block->compare, cache->block_size)) {
|
||||
if (!block->is_dirty && block->compare != NULL
|
||||
&& memcmp(block->current_data, block->compare, cache->block_size)) {
|
||||
dprintf("new block:\n");
|
||||
dump_block((const char *)block->current_data, 256, " ");
|
||||
dprintf("unchanged block:\n");
|
||||
@ -458,8 +462,10 @@ put_cached_block(block_cache *cache, cached_block *block)
|
||||
static void
|
||||
put_cached_block(block_cache *cache, off_t blockNumber)
|
||||
{
|
||||
if (blockNumber < 0 || blockNumber >= cache->max_blocks)
|
||||
panic("put_cached_block: invalid block number %lld (max %lld)", blockNumber, cache->max_blocks - 1);
|
||||
if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
|
||||
panic("put_cached_block: invalid block number %lld (max %lld)",
|
||||
blockNumber, cache->max_blocks - 1);
|
||||
}
|
||||
|
||||
cached_block *block = (cached_block *)hash_lookup(cache->hash, &blockNumber);
|
||||
if (block != NULL)
|
||||
@ -467,14 +473,29 @@ put_cached_block(block_cache *cache, off_t blockNumber)
|
||||
}
|
||||
|
||||
|
||||
static cached_block *
|
||||
get_cached_block(block_cache *cache, off_t blockNumber, bool *allocated, bool readBlock = true)
|
||||
{
|
||||
if (blockNumber < 0 || blockNumber >= cache->max_blocks)
|
||||
panic("get_cached_block: invalid block number %lld (max %lld)", blockNumber, cache->max_blocks - 1);
|
||||
/*!
|
||||
Retrieves the block \a blockNumber from the hash table, if it's already
|
||||
there, or reads it from the disk.
|
||||
|
||||
cached_block *block = (cached_block *)hash_lookup(cache->hash, &blockNumber);
|
||||
*allocated = false;
|
||||
\param _allocated tells you wether or not a new block has been allocated
|
||||
to satisfy your request.
|
||||
\param readBlock if \c false, the block will not be read in case it was
|
||||
not already in the cache. The block you retrieve may contain random
|
||||
data.
|
||||
*/
|
||||
static cached_block *
|
||||
get_cached_block(block_cache *cache, off_t blockNumber, bool *_allocated,
|
||||
bool readBlock = true)
|
||||
{
|
||||
if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
|
||||
panic("get_cached_block: invalid block number %lld (max %lld)",
|
||||
blockNumber, cache->max_blocks - 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cached_block *block = (cached_block *)hash_lookup(cache->hash,
|
||||
&blockNumber);
|
||||
*_allocated = false;
|
||||
|
||||
if (block == NULL) {
|
||||
// read block into cache
|
||||
@ -482,8 +503,10 @@ get_cached_block(block_cache *cache, off_t blockNumber, bool *allocated, bool re
|
||||
if (block == NULL)
|
||||
return NULL;
|
||||
|
||||
*allocated = true;
|
||||
hash_insert(cache->hash, block);
|
||||
*_allocated = true;
|
||||
} else {
|
||||
// TODO: currently, the data is always mapped in
|
||||
/*
|
||||
if (block->ref_count == 0 && block->current_data != NULL) {
|
||||
// see if the old block can be resurrected
|
||||
@ -496,15 +519,16 @@ get_cached_block(block_cache *cache, off_t blockNumber, bool *allocated, bool re
|
||||
if (block->current_data == NULL)
|
||||
return NULL;
|
||||
|
||||
*allocated = true;
|
||||
*_allocated = true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (*allocated && readBlock) {
|
||||
if (*_allocated && readBlock) {
|
||||
int32 blockSize = cache->block_size;
|
||||
|
||||
if (read_pos(cache->fd, blockNumber * blockSize, block->current_data, blockSize) < blockSize) {
|
||||
if (read_pos(cache->fd, blockNumber * blockSize, block->current_data,
|
||||
blockSize) < blockSize) {
|
||||
hash_remove(cache->hash, block);
|
||||
cache->FreeBlock(block);
|
||||
FATAL(("could not read block %Ld\n", blockNumber));
|
||||
@ -525,24 +549,29 @@ get_cached_block(block_cache *cache, off_t blockNumber, bool *allocated, bool re
|
||||
}
|
||||
|
||||
|
||||
/** Returns the writable block data for the requested blockNumber.
|
||||
* If \a cleared is true, the block is not read from disk; an empty block
|
||||
* is returned.
|
||||
* This is the only method to insert a block into a transaction. It makes
|
||||
* sure that the previous block contents are preserved in that case.
|
||||
*/
|
||||
/*!
|
||||
Returns the writable block data for the requested blockNumber.
|
||||
If \a cleared is true, the block is not read from disk; an empty block
|
||||
is returned.
|
||||
|
||||
This is the only method to insert a block into a transaction. It makes
|
||||
sure that the previous block contents are preserved in that case.
|
||||
*/
|
||||
static void *
|
||||
get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off_t length,
|
||||
int32 transactionID, bool cleared)
|
||||
get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base,
|
||||
off_t length, int32 transactionID, bool cleared)
|
||||
{
|
||||
TRACE(("get_writable_cached_block(blockNumber = %Ld, transaction = %ld)\n", blockNumber, transactionID));
|
||||
TRACE(("get_writable_cached_block(blockNumber = %Ld, transaction = %ld)\n",
|
||||
blockNumber, transactionID));
|
||||
|
||||
if (blockNumber < 0 || blockNumber >= cache->max_blocks)
|
||||
panic("get_writable_cached_block: invalid block number %lld (max %lld)", blockNumber, cache->max_blocks - 1);
|
||||
if (blockNumber < 0 || blockNumber >= cache->max_blocks) {
|
||||
panic("get_writable_cached_block: invalid block number %lld (max %lld)",
|
||||
blockNumber, cache->max_blocks - 1);
|
||||
}
|
||||
|
||||
bool allocated;
|
||||
cached_block *block = get_cached_block(cache, blockNumber, &allocated, !cleared);
|
||||
cached_block *block = get_cached_block(cache, blockNumber, &allocated,
|
||||
!cleared);
|
||||
if (block == NULL)
|
||||
return NULL;
|
||||
|
||||
@ -568,7 +597,8 @@ get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off
|
||||
// get new transaction
|
||||
cache_transaction *transaction = lookup_transaction(cache, transactionID);
|
||||
if (transaction == NULL) {
|
||||
panic("get_writable_cached_block(): invalid transaction %ld!\n", transactionID);
|
||||
panic("get_writable_cached_block(): invalid transaction %ld!\n",
|
||||
transactionID);
|
||||
put_cached_block(cache, block);
|
||||
return NULL;
|
||||
}
|
||||
@ -601,7 +631,7 @@ get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off
|
||||
// remember any previous contents for the parent transaction
|
||||
block->parent_data = cache->Allocate();
|
||||
if (block->parent_data == NULL) {
|
||||
// ToDo: maybe we should just continue the current transaction in this case...
|
||||
// TODO: maybe we should just continue the current transaction in this case...
|
||||
FATAL(("could not allocate parent\n"));
|
||||
put_cached_block(cache, block);
|
||||
return NULL;
|
||||
@ -621,20 +651,24 @@ get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off
|
||||
|
||||
|
||||
static status_t
|
||||
write_cached_block(block_cache *cache, cached_block *block, bool deleteTransaction)
|
||||
write_cached_block(block_cache *cache, cached_block *block,
|
||||
bool deleteTransaction)
|
||||
{
|
||||
cache_transaction *previous = block->previous_transaction;
|
||||
int32 blockSize = cache->block_size;
|
||||
|
||||
void *data = previous && block->original_data ? block->original_data : block->current_data;
|
||||
void *data = previous && block->original_data
|
||||
? block->original_data : block->current_data;
|
||||
// we first need to write back changes from previous transactions
|
||||
|
||||
TRACE(("write_cached_block(block %Ld)\n", block->block_number));
|
||||
|
||||
ssize_t written = write_pos(cache->fd, block->block_number * blockSize, data, blockSize);
|
||||
ssize_t written = write_pos(cache->fd, block->block_number * blockSize,
|
||||
data, blockSize);
|
||||
|
||||
if (written < blockSize) {
|
||||
FATAL(("could not write back block %Ld (%s)\n", block->block_number, strerror(errno)));
|
||||
FATAL(("could not write back block %Ld (%s)\n", block->block_number,
|
||||
strerror(errno)));
|
||||
return B_IO_ERROR;
|
||||
}
|
||||
|
||||
@ -649,13 +683,17 @@ write_cached_block(block_cache *cache, cached_block *block, bool deleteTransacti
|
||||
if (--previous->num_blocks == 0) {
|
||||
TRACE(("cache transaction %ld finished!\n", previous->id));
|
||||
|
||||
if (previous->notification_hook != NULL)
|
||||
previous->notification_hook(previous->id, previous->notification_data);
|
||||
if (previous->notification_hook != NULL) {
|
||||
previous->notification_hook(previous->id,
|
||||
previous->notification_data);
|
||||
}
|
||||
|
||||
if (deleteTransaction)
|
||||
if (deleteTransaction) {
|
||||
hash_remove(cache->transaction_hash, previous);
|
||||
delete_transaction(cache, previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -677,8 +715,10 @@ cache_start_transaction(void *_cache)
|
||||
block_cache *cache = (block_cache *)_cache;
|
||||
BenaphoreLocker locker(&cache->lock);
|
||||
|
||||
if (cache->last_transaction && cache->last_transaction->open)
|
||||
panic("last transaction (%ld) still open!\n", cache->last_transaction->id);
|
||||
if (cache->last_transaction && cache->last_transaction->open) {
|
||||
panic("last transaction (%ld) still open!\n",
|
||||
cache->last_transaction->id);
|
||||
}
|
||||
|
||||
cache_transaction *transaction = new(nothrow) cache_transaction;
|
||||
if (transaction == NULL)
|
||||
@ -706,17 +746,21 @@ cache_sync_transaction(void *_cache, int32 id)
|
||||
hash_open(cache->transaction_hash, &iterator);
|
||||
|
||||
cache_transaction *transaction;
|
||||
while ((transaction = (cache_transaction *)hash_next(cache->transaction_hash, &iterator)) != NULL) {
|
||||
// ToDo: fix hash interface to make this easier
|
||||
while ((transaction = (cache_transaction *)hash_next(
|
||||
cache->transaction_hash, &iterator)) != NULL) {
|
||||
// close all earlier transactions which haven't been closed yet
|
||||
|
||||
if (transaction->id <= id && !transaction->open) {
|
||||
// write back all of their remaining dirty blocks
|
||||
while (transaction->num_blocks > 0) {
|
||||
status = write_cached_block(cache, transaction->blocks.Head(), false);
|
||||
status = write_cached_block(cache, transaction->blocks.Head(),
|
||||
false);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
hash_remove_current(cache->transaction_hash, &iterator);
|
||||
delete_transaction(cache, transaction);
|
||||
hash_rewind(cache->transaction_hash, &iterator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,7 +770,8 @@ cache_sync_transaction(void *_cache, int32 id)
|
||||
|
||||
|
||||
extern "C" status_t
|
||||
cache_end_transaction(void *_cache, int32 id, transaction_notification_hook hook, void *data)
|
||||
cache_end_transaction(void *_cache, int32 id,
|
||||
transaction_notification_hook hook, void *data)
|
||||
{
|
||||
block_cache *cache = (block_cache *)_cache;
|
||||
BenaphoreLocker locker(&cache->lock);
|
||||
@ -814,16 +859,17 @@ cache_abort_transaction(void *_cache, int32 id)
|
||||
block->transaction = NULL;
|
||||
}
|
||||
|
||||
hash_remove(cache->transaction_hash, transaction);
|
||||
delete_transaction(cache, transaction);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/** Acknowledges the current parent transaction, and starts a new transaction
|
||||
* from its sub transaction.
|
||||
* The new transaction also gets a new transaction ID.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Acknowledges the current parent transaction, and starts a new transaction
|
||||
from its sub transaction.
|
||||
The new transaction also gets a new transaction ID.
|
||||
*/
|
||||
extern "C" int32
|
||||
cache_detach_sub_transaction(void *_cache, int32 id,
|
||||
transaction_notification_hook hook, void *data)
|
||||
@ -869,7 +915,8 @@ cache_detach_sub_transaction(void *_cache, int32 id,
|
||||
cache->Free(block->original_data);
|
||||
block->original_data = NULL;
|
||||
}
|
||||
if (block->parent_data != NULL && block->parent_data != block->current_data) {
|
||||
if (block->parent_data != NULL
|
||||
&& block->parent_data != block->current_data) {
|
||||
// we need to move this block over to the new transaction
|
||||
block->original_data = block->parent_data;
|
||||
if (last == NULL)
|
||||
@ -924,7 +971,8 @@ cache_abort_sub_transaction(void *_cache, int32 id)
|
||||
if (block->original_data != NULL) {
|
||||
// the parent transaction didn't change the block, but the sub
|
||||
// transaction did - we need to revert from the original data
|
||||
memcpy(block->current_data, block->original_data, cache->block_size);
|
||||
memcpy(block->current_data, block->original_data,
|
||||
cache->block_size);
|
||||
}
|
||||
} else if (block->parent_data != block->current_data) {
|
||||
// the block has been changed and must be restored
|
||||
@ -985,8 +1033,8 @@ cache_start_sub_transaction(void *_cache, int32 id)
|
||||
|
||||
|
||||
extern "C" status_t
|
||||
cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie, off_t *_blockNumber,
|
||||
void **_data, void **_unchangedData)
|
||||
cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie,
|
||||
off_t *_blockNumber, void **_data, void **_unchangedData)
|
||||
{
|
||||
cached_block *block = (cached_block *)*_cookie;
|
||||
block_cache *cache = (block_cache *)_cache;
|
||||
@ -1063,7 +1111,8 @@ block_cache_delete(void *_cache, bool allowWrites)
|
||||
|
||||
uint32 cookie = 0;
|
||||
cached_block *block;
|
||||
while ((block = (cached_block *)hash_remove_first(cache->hash, &cookie)) != NULL) {
|
||||
while ((block = (cached_block *)hash_remove_first(cache->hash,
|
||||
&cookie)) != NULL) {
|
||||
cache->FreeBlock(block);
|
||||
}
|
||||
|
||||
@ -1071,7 +1120,8 @@ block_cache_delete(void *_cache, bool allowWrites)
|
||||
|
||||
cookie = 0;
|
||||
cache_transaction *transaction;
|
||||
while ((transaction = (cache_transaction *)hash_remove_first(cache->transaction_hash, &cookie)) != NULL) {
|
||||
while ((transaction = (cache_transaction *)hash_remove_first(
|
||||
cache->transaction_hash, &cookie)) != NULL) {
|
||||
delete transaction;
|
||||
}
|
||||
|
||||
@ -1082,7 +1132,8 @@ block_cache_delete(void *_cache, bool allowWrites)
|
||||
extern "C" void *
|
||||
block_cache_create(int fd, off_t numBlocks, size_t blockSize, bool readOnly)
|
||||
{
|
||||
block_cache *cache = new(nothrow) block_cache(fd, numBlocks, blockSize, readOnly);
|
||||
block_cache *cache = new(nothrow) block_cache(fd, numBlocks, blockSize,
|
||||
readOnly);
|
||||
if (cache == NULL)
|
||||
return NULL;
|
||||
|
||||
@ -1212,12 +1263,20 @@ block_cache_get(void *_cache, off_t blockNumber)
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Changes the internal status of a writable block to \a dirty. This can be
|
||||
helpful in case you realize you don't need to change that block anymore
|
||||
for whatever reason.
|
||||
|
||||
Note, you must only use this function on blocks that were acquired
|
||||
writable!
|
||||
*/
|
||||
extern "C" status_t
|
||||
block_cache_set_dirty(void *_cache, off_t blockNumber, bool isDirty, int32 transaction)
|
||||
block_cache_set_dirty(void *_cache, off_t blockNumber, bool dirty,
|
||||
int32 transaction)
|
||||
{
|
||||
// not yet implemented
|
||||
// Note, you must only use this function on blocks that were acquired writable!
|
||||
if (isDirty)
|
||||
// TODO: not yet implemented
|
||||
if (dirty)
|
||||
panic("block_cache_set_dirty(): not yet implemented that way!\n");
|
||||
|
||||
return B_OK;
|
||||
|
Loading…
Reference in New Issue
Block a user