#include "BlockCache.h"
#include <new>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fs_cache.h>
#include "Block.h"
#include "Debug.h"
#include "reiserfs.h"
using std::nothrow;
\class BlockCache
\brief Implements a cache for disk blocks.
The central methods are GetBlock() and PutBlock(). The former one
requests a certain block and the latter tells the cache, that the caller
is done with the block. When a block is unused it is either kept in
memory until the next time it is needed or until its memory is needed
for another block.
*/
BlockCache::BlockCache()
:
fDevice(-1),
fBlockSize(0),
fBlockCount(0),
fLock(),
fBlocks(),
fReads(0),
fBlockGets(0),
fBlockReleases(0)
{
}
BlockCache::~BlockCache()
{
fLock.Lock();
for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) {
if (block->_GetRefCount() > 0) {
INFORM(("WARNING: block not put: %p (ref count: %" B_PRId32 ")\n",
block, block->_GetRefCount()));
}
delete block;
}
PRINT(("statistics: %" B_PRId64 " block reads\n", fReads));
PRINT(("statistics: %" B_PRId64 " block gets\n", fBlockGets));
PRINT(("statistics: %" B_PRId64 " block releases\n", fBlockReleases));
if (fCacheHandle)
block_cache_delete(fCacheHandle, false);
fLock.Unlock();
}
status_t
BlockCache::Init(int device, uint64 blockCount, uint32 blockSize)
{
if (device < 0 || blockSize <= 0)
return B_BAD_VALUE;
fDevice = device;
fBlockSize = blockSize;
fBlockCount = blockCount;
fCacheHandle = block_cache_create(fDevice, blockCount, blockSize, true);
if (!fCacheHandle)
return B_ERROR;
return B_OK;
}
status_t
BlockCache::GetBlock(Block *block)
{
status_t error = (block ? B_OK : B_BAD_VALUE);
if (error == B_OK) {
fLock.Lock();
if (fBlocks.HasItem(block))
block->_Get();
else
error = B_BAD_VALUE;
fLock.Unlock();
}
return error;
}
status_t
BlockCache::GetBlock(uint64 number, Block **result)
{
if (!result || number >= fBlockCount)
return B_BAD_VALUE;
fLock.Lock();
status_t error = B_OK;
Block *block = _FindBlock(number);
if (!block) {
error = _ReadBlock(number, &block);
if (error == B_OK)
fBlocks.AddItem(block);
}
if (error == B_OK) {
block->_Get();
*result = block;
}
fLock.Unlock();
return error;
}
void
BlockCache::PutBlock(Block *block)
{
fLock.Lock();
if (block && fBlocks.HasItem(block)) {
if (block->_Put()) {
fBlocks.RemoveItem(block);
delete block;
}
}
fLock.Unlock();
}
Block *
BlockCache::_FindBlock(uint64 number)
{
for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) {
if (block->GetNumber() == number)
return block;
}
return NULL;
}
status_t
BlockCache::_ReadBlock(uint64 number, Block **result)
{
fReads++;
Block *block = new(nothrow) Block;
if (!block)
return B_NO_MEMORY;
status_t error = block->_SetTo(this, number);
if (error == B_OK)
*result = block;
else if (block)
delete block;
return error;
}
void *
BlockCache::_GetBlock(off_t number) const
{
fBlockGets++;
return const_cast<void*>(block_cache_get(fCacheHandle, number));
}
void
BlockCache::_ReleaseBlock(off_t number, void *data) const
{
fBlockReleases++;
block_cache_put(fCacheHandle, number);
}