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 : fDevice(-1), 55 fBlockSize(0), 56 fBlockCount(0), 57 fLock(), 58 fBlocks(), 59 fReads(0), 60 fBlockGets(0), 61 fBlockReleases(0) 62 { 63 } 64 65 // destructor 66 BlockCache::~BlockCache() 67 { 68 fLock.Lock(); 69 for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) { 70 if (block->_GetRefCount() > 0) { 71 INFORM(("WARNING: block not put: %p (ref count: %ld)\n", block, 72 block->_GetRefCount())); 73 } 74 delete block; 75 } 76 PRINT(("statistics: %Ld block reads\n", fReads)); 77 PRINT(("statistics: %Ld block gets\n", fBlockGets)); 78 PRINT(("statistics: %Ld block releases\n", fBlockReleases)); 79 if (fCacheHandle) 80 block_cache_delete(fCacheHandle, false); 81 fLock.Unlock(); 82 } 83 84 // Init 85 status_t 86 BlockCache::Init(int device, uint64 blockCount, uint32 blockSize) 87 { 88 if (device < 0 || blockSize <= 0) 89 return B_BAD_VALUE; 90 91 fDevice = device; 92 fBlockSize = blockSize; 93 fBlockCount = blockCount; 94 95 // init block cache 96 fCacheHandle = block_cache_create(fDevice, blockCount, blockSize, true); 97 if (!fCacheHandle) 98 return B_ERROR; 99 100 return B_OK; 101 } 102 103 // GetBlock 104 status_t 105 BlockCache::GetBlock(Block *block) 106 { 107 status_t error = (block ? B_OK : B_BAD_VALUE); 108 if (error == B_OK) { 109 fLock.Lock(); 110 if (fBlocks.HasItem(block)) 111 block->_Get(); 112 else 113 error = B_BAD_VALUE; 114 fLock.Unlock(); 115 } 116 return error; 117 } 118 119 // GetBlock 120 status_t 121 BlockCache::GetBlock(uint64 number, Block **result) 122 { 123 124 if (!result || number >= fBlockCount) 125 return B_BAD_VALUE; 126 127 fLock.Lock(); 128 129 // find the block in the cache 130 status_t error = B_OK; 131 Block *block = _FindBlock(number); 132 if (!block) { 133 // not found, read it from disk 134 error = _ReadBlock(number, &block); 135 if (error == B_OK) 136 fBlocks.AddItem(block); 137 } 138 139 // increase the block's reference counter 140 if (error == B_OK) { 141 block->_Get(); 142 *result = block; 143 } 144 145 fLock.Unlock(); 146 return error; 147 } 148 149 // PutBlock 150 void 151 BlockCache::PutBlock(Block *block) 152 { 153 fLock.Lock(); 154 if (block && fBlocks.HasItem(block)) { 155 if (block->_Put()) { 156 fBlocks.RemoveItem(block); 157 delete block; 158 } 159 } 160 fLock.Unlock(); 161 } 162 163 // _FindBlock 164 Block * 165 BlockCache::_FindBlock(uint64 number) 166 { 167 for (int32 i = 0; Block *block = fBlocks.ItemAt(i); i++) { 168 if (block->GetNumber() == number) 169 return block; 170 } 171 return NULL; 172 } 173 174 // _ReadBlock 175 status_t 176 BlockCache::_ReadBlock(uint64 number, Block **result) 177 { 178 fReads++; // statistics 179 180 // get a free block and read the block data 181 Block *block = new(nothrow) Block; 182 if (!block) 183 return B_NO_MEMORY; 184 185 status_t error = block->_SetTo(this, number); 186 187 // set the result / cleanup on error 188 if (error == B_OK) 189 *result = block; 190 else if (block) 191 delete block; 192 return error; 193 } 194 195 // _GetBlock 196 void * 197 BlockCache::_GetBlock(off_t number) const 198 { 199 fBlockGets++; // statistics 200 return const_cast<void*>(block_cache_get(fCacheHandle, number)); 201 } 202 203 // _ReleaseBlock 204 void 205 BlockCache::_ReleaseBlock(off_t number, void *data) const 206 { 207 fBlockReleases++; // statistics 208 block_cache_put(fCacheHandle, number); 209 } 210 211