1// BlockCache.cpp 2// 3// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) 4// 5// This program is free software; you can redistribute it and/or modify 6// it under the terms of the GNU General Public License as published by 7// the Free Software Foundation; either version 2 of the License, or 8// (at your option) any later version. 9// 10// This program is distributed in the hope that it will be useful, 11// but WITHOUT ANY WARRANTY; without even the implied warranty of 12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13// GNU General Public License for more details. 14// 15// You should have received a copy of the GNU General Public License 16// along with this program; if not, write to the Free Software 17// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18// 19// You can alternatively use *this file* under the terms of the the MIT 20// license included in this package. 21 22#include "BlockCache.h" 23 24#include <new> 25 26#include <errno.h> 27#include <fcntl.h> 28#include <stdio.h> 29#include <stdlib.h> 30#include <string.h> 31#include <unistd.h> 32 33#include <fs_cache.h> 34 35#include "Block.h" 36#include "Debug.h" 37#include "reiserfs.h" 38 39using std::nothrow; 40 41/*! 42 \class BlockCache 43 \brief Implements a cache for disk blocks. 44 45 The central methods are GetBlock() and PutBlock(). The former one 46 requests a certain block and the latter tells the cache, that the caller 47 is done with the block. When a block is unused it is either kept in 48 memory until the next time it is needed or until its memory is needed 49 for another block. 50*/ 51 52// constructor 53BlockCache::BlockCache() 54 : 55 fDevice(-1), 56 fBlockSize(0), 57 fBlockCount(0), 58 fLock(), 59 fBlocks(), 60 fReads(0), 61 fBlockGets(0), 62 fBlockReleases(0) 63{ 64} 65 66// destructor 67BlockCache::~BlockCache() 68{ 69 fLock.Lock(); 70 for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) { 71 if (block->_GetRefCount() > 0) { 72 INFORM(("WARNING: block not put: %p (ref count: %" B_PRId32 ")\n", 73 block, block->_GetRefCount())); 74 } 75 delete block; 76 } 77 PRINT(("statistics: %" B_PRId64 " block reads\n", fReads)); 78 PRINT(("statistics: %" B_PRId64 " block gets\n", fBlockGets)); 79 PRINT(("statistics: %" B_PRId64 " block releases\n", fBlockReleases)); 80 if (fCacheHandle) 81 block_cache_delete(fCacheHandle, false); 82 fLock.Unlock(); 83} 84 85// Init 86status_t 87BlockCache::Init(int device, uint64 blockCount, uint32 blockSize) 88{ 89 if (device < 0 || blockSize <= 0) 90 return B_BAD_VALUE; 91 92 fDevice = device; 93 fBlockSize = blockSize; 94 fBlockCount = blockCount; 95 96 // init block cache 97 fCacheHandle = block_cache_create(fDevice, blockCount, blockSize, true); 98 if (!fCacheHandle) 99 return B_ERROR; 100 101 return B_OK; 102} 103 104// GetBlock 105status_t 106BlockCache::GetBlock(Block *block) 107{ 108 status_t error = (block ? B_OK : B_BAD_VALUE); 109 if (error == B_OK) { 110 fLock.Lock(); 111 if (fBlocks.HasItem(block)) 112 block->_Get(); 113 else 114 error = B_BAD_VALUE; 115 fLock.Unlock(); 116 } 117 return error; 118} 119 120// GetBlock 121status_t 122BlockCache::GetBlock(uint64 number, Block **result) 123{ 124 125 if (!result || number >= fBlockCount) 126 return B_BAD_VALUE; 127 128 fLock.Lock(); 129 130 // find the block in the cache 131 status_t error = B_OK; 132 Block *block = _FindBlock(number); 133 if (!block) { 134 // not found, read it from disk 135 error = _ReadBlock(number, &block); 136 if (error == B_OK) 137 fBlocks.AddItem(block); 138 } 139 140 // increase the block's reference counter 141 if (error == B_OK) { 142 block->_Get(); 143 *result = block; 144 } 145 146 fLock.Unlock(); 147 return error; 148} 149 150// PutBlock 151void 152BlockCache::PutBlock(Block *block) 153{ 154 fLock.Lock(); 155 if (block && fBlocks.HasItem(block)) { 156 if (block->_Put()) { 157 fBlocks.RemoveItem(block); 158 delete block; 159 } 160 } 161 fLock.Unlock(); 162} 163 164// _FindBlock 165Block * 166BlockCache::_FindBlock(uint64 number) 167{ 168 for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) { 169 if (block->GetNumber() == number) 170 return block; 171 } 172 return NULL; 173} 174 175// _ReadBlock 176status_t 177BlockCache::_ReadBlock(uint64 number, Block **result) 178{ 179 fReads++; // statistics 180 181 // get a free block and read the block data 182 Block *block = new(nothrow) Block; 183 if (!block) 184 return B_NO_MEMORY; 185 186 status_t error = block->_SetTo(this, number); 187 188 // set the result / cleanup on error 189 if (error == B_OK) 190 *result = block; 191 else if (block) 192 delete block; 193 return error; 194} 195 196// _GetBlock 197void * 198BlockCache::_GetBlock(off_t number) const 199{ 200 fBlockGets++; // statistics 201 return const_cast<void*>(block_cache_get(fCacheHandle, number)); 202} 203 204// _ReleaseBlock 205void 206BlockCache::_ReleaseBlock(off_t number, void *data) const 207{ 208 fBlockReleases++; // statistics 209 block_cache_put(fCacheHandle, number); 210} 211 212