xref: /haiku/src/add-ons/kernel/file_systems/reiserfs/BlockCache.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
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