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