Moved the block cache to this file (from ../vm/cache.cpp).
It still contains a very dump version of a BeOS compatible block cache. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8818 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+348
@@ -0,0 +1,348 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2004, Axel Dörfler, [email protected]. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <cache.h>
|
||||||
|
#include <khash.h>
|
||||||
|
#include <lock.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Note, this is just a dump and simple cache implementation targeted to be
|
||||||
|
* compatible with the one found in BeOS.
|
||||||
|
* This won't be the cache we end up using in R1, seriously :-)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
struct cache_entry {
|
||||||
|
cache_entry *next; // next in hash
|
||||||
|
off_t block_number;
|
||||||
|
void *data;
|
||||||
|
int32 ref_count;
|
||||||
|
int32 lock;
|
||||||
|
bool is_dirty;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cache {
|
||||||
|
hash_table *hash;
|
||||||
|
mutex lock;
|
||||||
|
off_t max_blocks;
|
||||||
|
size_t block_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int32 kNumCaches = 16;
|
||||||
|
struct cache sCaches[kNumCaches];
|
||||||
|
// we can cache the first 16 fds (I said we were dumb, right?)
|
||||||
|
|
||||||
|
|
||||||
|
class MutexLocker {
|
||||||
|
public:
|
||||||
|
MutexLocker(int fd)
|
||||||
|
: fMutex(NULL)
|
||||||
|
{
|
||||||
|
if (fd < 0 || fd >= kNumCaches)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fMutex = &sCaches[fd].lock;
|
||||||
|
mutex_lock(fMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
~MutexLocker()
|
||||||
|
{
|
||||||
|
if (fMutex != NULL)
|
||||||
|
mutex_unlock(fMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t InitCheck()
|
||||||
|
{
|
||||||
|
return fMutex != NULL ? B_OK : B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutex *fMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
cache_entry_compare(void *_cacheEntry, const void *_block)
|
||||||
|
{
|
||||||
|
cache_entry *cacheEntry = (cache_entry *)_cacheEntry;
|
||||||
|
const off_t *block = (const off_t *)_block;
|
||||||
|
|
||||||
|
return cacheEntry->block_number - *block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32
|
||||||
|
cache_entry_hash(void *_cacheEntry, const void *_block, uint32 range)
|
||||||
|
{
|
||||||
|
cache_entry *cacheEntry = (cache_entry *)_cacheEntry;
|
||||||
|
const off_t *block = (const off_t *)_block;
|
||||||
|
|
||||||
|
if (cacheEntry != NULL)
|
||||||
|
return cacheEntry->block_number % range;
|
||||||
|
|
||||||
|
return *block % range;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
write_cache_entry(int fd, cache_entry *entry)
|
||||||
|
{
|
||||||
|
ssize_t blockSize = sCaches[fd].block_size;
|
||||||
|
|
||||||
|
return write_pos(fd, entry->block_number * blockSize,
|
||||||
|
entry->data, blockSize) < blockSize ? B_ERROR : B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
put_cache_entry(int fd, cache_entry *entry)
|
||||||
|
{
|
||||||
|
if (entry == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (--entry->ref_count > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// write out entry if necessary
|
||||||
|
if (entry->is_dirty
|
||||||
|
&& write_cache_entry(fd, entry) != B_OK)
|
||||||
|
panic("could not write back entry\n");
|
||||||
|
|
||||||
|
hash_remove(sCaches[fd].hash, entry);
|
||||||
|
free(entry->data);
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cache_entry *
|
||||||
|
new_cache_entry(int fd, off_t blockNumber, size_t blockSize)
|
||||||
|
{
|
||||||
|
cache_entry *entry = (cache_entry *)malloc(sizeof(cache_entry));
|
||||||
|
if (entry == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
entry->data = malloc(blockSize);
|
||||||
|
if (entry->data == NULL) {
|
||||||
|
free(entry);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sCaches[fd].block_size = blockSize;
|
||||||
|
// store block size (once would be sufficient, though)
|
||||||
|
|
||||||
|
entry->block_number = blockNumber;
|
||||||
|
entry->ref_count = 1;
|
||||||
|
entry->lock = 0;
|
||||||
|
entry->is_dirty = false;
|
||||||
|
|
||||||
|
hash_insert(sCaches[fd].hash, entry);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cache_entry *
|
||||||
|
lookup_cache_entry(int fd, off_t blockNumber)
|
||||||
|
{
|
||||||
|
cache_entry *entry = (cache_entry *)hash_lookup(sCaches[fd].hash, &blockNumber);
|
||||||
|
if (entry == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
entry->ref_count++;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark -
|
||||||
|
// public BeOS compatible interface
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
force_cache_flush(int dev, int prefer_log_blocks)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
flush_blocks(int dev, off_t bnum, int nblocks)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
flush_device(int dev, int warn_locked)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
init_cache_for_device(int fd, off_t max_blocks)
|
||||||
|
{
|
||||||
|
if (fd < 0 || fd >= kNumCaches)
|
||||||
|
return B_ERROR;
|
||||||
|
if (sCaches[fd].hash != NULL)
|
||||||
|
return B_BUSY;
|
||||||
|
|
||||||
|
sCaches[fd].hash = hash_init(32, 0, &cache_entry_compare, &cache_entry_hash);
|
||||||
|
return mutex_init(&sCaches[fd].lock, "block cache");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
remove_cached_device_blocks(int fd, int allowWrite)
|
||||||
|
{
|
||||||
|
if (fd < 0 || fd >= kNumCaches || sCaches[fd].hash == NULL)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
if (allowWrite) {
|
||||||
|
hash_iterator iterator;
|
||||||
|
hash_open(sCaches[fd].hash, &iterator);
|
||||||
|
|
||||||
|
cache_entry *entry;
|
||||||
|
while ((entry = (cache_entry *)hash_next(sCaches[fd].hash, &iterator)) != NULL) {
|
||||||
|
// write out entry if necessary
|
||||||
|
if (entry->is_dirty
|
||||||
|
&& write_cache_entry(fd, entry) != B_OK)
|
||||||
|
panic("write failed!\n");
|
||||||
|
}
|
||||||
|
hash_close(sCaches[fd].hash, &iterator, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_uninit(sCaches[fd].hash);
|
||||||
|
mutex_destroy(&sCaches[fd].lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void *
|
||||||
|
get_block(int fd, off_t blockNumber, int blockSize)
|
||||||
|
{
|
||||||
|
MutexLocker locker(fd);
|
||||||
|
if (locker.InitCheck() != B_OK)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
cache_entry *entry = lookup_cache_entry(fd, blockNumber);
|
||||||
|
if (entry == NULL) {
|
||||||
|
// read entry into cache
|
||||||
|
entry = new_cache_entry(fd, blockNumber, blockSize);
|
||||||
|
if (entry == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (read_pos(fd, blockNumber * blockSize, entry->data, blockSize) < blockSize) {
|
||||||
|
free(entry->data);
|
||||||
|
free(entry);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->lock++;
|
||||||
|
|
||||||
|
return entry->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void *
|
||||||
|
get_empty_block(int fd, off_t blockNumber, int blockSize)
|
||||||
|
{
|
||||||
|
MutexLocker locker(fd);
|
||||||
|
if (locker.InitCheck() != B_OK)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
cache_entry *entry = lookup_cache_entry(fd, blockNumber);
|
||||||
|
if (entry == NULL) {
|
||||||
|
// create new cache entry
|
||||||
|
entry = new_cache_entry(fd, blockNumber, blockSize);
|
||||||
|
if (entry == NULL)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(entry->data, 0, blockSize);
|
||||||
|
|
||||||
|
entry->lock++;
|
||||||
|
|
||||||
|
return entry->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
release_block(int fd, off_t blockNumber)
|
||||||
|
{
|
||||||
|
MutexLocker locker(fd);
|
||||||
|
if (locker.InitCheck() != B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
cache_entry *entry = lookup_cache_entry(fd, blockNumber);
|
||||||
|
if (entry == NULL)
|
||||||
|
panic("release_block() called on block %Ld that was not cached\n", blockNumber);
|
||||||
|
|
||||||
|
entry->lock--;
|
||||||
|
put_cache_entry(fd, entry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
mark_blocks_dirty(int fd, off_t blockNumber, int numBlocks)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
cached_read(int fd, off_t blockNumber, void *data, off_t numBlocks, int blockSize)
|
||||||
|
{
|
||||||
|
MutexLocker locker(fd);
|
||||||
|
if (locker.InitCheck() != B_OK)
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
ssize_t bytes = numBlocks * blockSize;
|
||||||
|
if (read_pos(fd, blockNumber * blockSize, data, bytes) < bytes)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// check if there are any cached blocks in that range shadowing the device contents
|
||||||
|
|
||||||
|
for (off_t blockOffset = 0; blockOffset < numBlocks; blockOffset++) {
|
||||||
|
cache_entry *entry = lookup_cache_entry(fd, blockNumber + blockOffset);
|
||||||
|
if (entry != NULL) {
|
||||||
|
if (entry->is_dirty)
|
||||||
|
memcpy((void *)((addr_t)data + blockOffset * blockSize), entry->data, blockSize);
|
||||||
|
put_cache_entry(fd, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
cached_write(int fd, off_t blockNumber, const void *data, off_t numBlocks, int blockSize)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
cached_write_locked(int fd, off_t blockNumber, const void *data, off_t numBlocks, int blockSize)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" int
|
||||||
|
set_blocks_info(int fd, off_t *blocks, int numBlocks,
|
||||||
|
void (*func)(off_t bnum, size_t nblocks, void *arg),
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user