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 39 using 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 53 BlockCache::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 67 BlockCache::~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 86 status_t 87 BlockCache::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 105 status_t 106 BlockCache::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 121 status_t 122 BlockCache::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 151 void 152 BlockCache::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 165 Block * 166 BlockCache::_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 176 status_t 177 BlockCache::_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 197 void * 198 BlockCache::_GetBlock(off_t number) const 199 { 200 fBlockGets++; // statistics 201 return const_cast<void*>(block_cache_get(fCacheHandle, number)); 202 } 203 204 // _ReleaseBlock 205 void 206 BlockCache::_ReleaseBlock(off_t number, void *data) const 207 { 208 fBlockReleases++; // statistics 209 block_cache_put(fCacheHandle, number); 210 } 211 212