xref: /haiku/src/add-ons/kernel/file_systems/reiserfs/BlockCache.cpp (revision f91802873f53c5bc6e65030778078597c2e56300)
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
BlockCache()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
~BlockCache()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
Init(int device,uint64 blockCount,uint32 blockSize)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
GetBlock(Block * block)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
GetBlock(uint64 number,Block ** result)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
PutBlock(Block * block)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 *
_FindBlock(uint64 number)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
_ReadBlock(uint64 number,Block ** result)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 *
_GetBlock(off_t number) const198 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
_ReleaseBlock(off_t number,void * data) const206 BlockCache::_ReleaseBlock(off_t number, void *data) const
207 {
208 	fBlockReleases++;	// statistics
209 	block_cache_put(fCacheHandle, number);
210 }
211 
212