Renamed system/core to system/kernel.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12360 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-04-13 13:22:10 +00:00
parent 5af32e7526
commit 2d690920ac
212 changed files with 0 additions and 0 deletions
+240
View File
@@ -0,0 +1,240 @@
/*
** Copyright 2004, Axel Dörfler, [email protected]. All rights reserved.
** Distributed under the terms of the Haiku License.
*/
/** The BlockMap stores offset:address pairs; you can map an offset to a specific address.
* It has been designed to contain few and mostly contiguous offset mappings - it is used
* by the file cache to keep track about which blocks of the file are already in memory.
* The offsets may spread over a very large amount.
*
* Internally, it stores small and contiguous address arrays of a certain size, and
* accesses those using a hash table. Address values of NULL are equal to non existing
* mappings; that value cannot be stored. At the current size, each hash entry can
* map 60 addresses which corresponds to a file range of 240 kB.
* It currently does not do any locking; it assumes a safe environment which you are
* responsible for to create when you call its functions.
*/
#include "BlockMap.h"
#include <KernelExport.h>
#include <util/kernel_cpp.h>
#include <stdlib.h>
#include <string.h>
//#define TRACE_BLOCK_MAP
#ifdef TRACE_BLOCK_MAP
# define TRACE(x) dprintf x
#else
# define TRACE(x)
#endif
// ToDo: when we have a better allocator, change the number of addresses to a power of two
// currently, this structure takes 256 bytes total
struct BlockMap::block_entry {
block_entry *next;
uint32 used;
off_t offset;
addr_t address[60];
};
#define BLOCK_ARRAY_SIZE (sizeof(BlockMap::block_entry::address) / sizeof(addr_t))
static inline off_t
to_block_entry_offset(off_t offset, uint32 &index)
{
// ToDo: improve this once we have a power of two array size
off_t baseOffset = (offset / BLOCK_ARRAY_SIZE) * BLOCK_ARRAY_SIZE;
index = uint32(offset - baseOffset);
return baseOffset;
}
static int
block_entry_compare(void *_entry, const void *_offset)
{
BlockMap::block_entry *entry = (BlockMap::block_entry *)_entry;
const off_t *offset = (const off_t *)_offset;
return entry->offset - *offset;
}
static uint32
block_entry_hash(void *_entry, const void *_offset, uint32 range)
{
BlockMap::block_entry *entry = (BlockMap::block_entry *)_entry;
const off_t *offset = (const off_t *)_offset;
if (entry != NULL)
return entry->offset % range;
return *offset % range;
}
// #pragma mark -
BlockMap::BlockMap(off_t size)
:
fSize(size)
{
fHashTable = hash_init(16, 0, &block_entry_compare, &block_entry_hash);
}
BlockMap::~BlockMap()
{
}
/** Checks wether or not the construction of the BlockMap were successful.
*/
status_t
BlockMap::InitCheck() const
{
return fHashTable != NULL ? B_OK : B_NO_MEMORY;
}
/** Sets the size of the block map - all existing entries beyond this size will be
* removed from the map, and their memory is freed.
*/
void
BlockMap::SetSize(off_t size)
{
TRACE(("BlockMap::SetSize(%Ld)\n", size));
if (size >= fSize) {
// nothing to do
fSize = size;
return;
}
// ToDo: remove all mappings beyond the file size
}
/** Upon successful exit which is indicated by a return value of B_OK, the "_entry"
* argument points to a block_entry structure containing the data for the given
* offset.
* The offset must have been normalized to the base offset values of a block entry
* already.
*/
status_t
BlockMap::GetBlockEntry(off_t baseOffset, block_entry **_entry)
{
block_entry *entry = (block_entry *)hash_lookup(fHashTable, &baseOffset);
if (entry == NULL)
return B_ENTRY_NOT_FOUND;
*_entry = entry;
return B_OK;
}
status_t
BlockMap::Remove(off_t offset, off_t count)
{
TRACE(("BlockMap::Remove(offset = %Ld, count = %Ld)\n", offset, count));
uint32 index;
off_t baseOffset = to_block_entry_offset(offset, index);
block_entry *entry;
while (count > 0) {
int32 max = min_c(BLOCK_ARRAY_SIZE, index + count);
int32 blocks = max - index;
if (GetBlockEntry(baseOffset, &entry) == B_OK) {
for (int32 i = index; i < max; i++) {
if (entry->address[i] != NULL)
entry->used--;
entry->address[i] = NULL;
}
if (entry->used == 0) {
// release entry if it's no longer used
hash_remove(fHashTable, entry);
free(entry);
}
}
baseOffset += BLOCK_ARRAY_SIZE;
count -= blocks;
index = 0;
}
return B_OK;
}
status_t
BlockMap::Set(off_t offset, addr_t address)
{
TRACE(("BlockMap::Set(offset = %Ld, address = %08lx)\n", offset, address));
uint32 index;
off_t baseOffset = to_block_entry_offset(offset, index);
block_entry *entry;
if (GetBlockEntry(baseOffset, &entry) == B_OK) {
// the block already exists, we just need to fill in our new address
if (entry->address[index] == NULL && address != NULL)
entry->used++;
else if (entry->address[index] != NULL && address == NULL)
entry->used--;
entry->address[index] = address;
return B_OK;
}
// allocate new block and fill it
entry = (block_entry *)malloc(sizeof(struct block_entry));
if (entry == NULL)
return B_NO_MEMORY;
memset(entry->address, 0, sizeof(entry->address));
entry->used = 1;
entry->offset = baseOffset;
hash_insert(fHashTable, entry);
entry->address[index] = address;
return B_OK;
}
status_t
BlockMap::Get(off_t offset, addr_t &address)
{
TRACE(("BlockMap::Get(offset = %Ld)\n", offset));
uint32 index;
off_t baseOffset = to_block_entry_offset(offset, index);
block_entry *entry;
if (GetBlockEntry(baseOffset, &entry) == B_OK
&& entry->address[index] != NULL) {
address = entry->address[index];
return B_OK;
}
return B_ENTRY_NOT_FOUND;
}
+35
View File
@@ -0,0 +1,35 @@
/*
* Copyright 2004-2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef BLOCK_MAP_H
#define BLOCK_MAP_H
#include <OS.h>
#include <util/khash.h>
class BlockMap {
public:
BlockMap(off_t size);
~BlockMap();
status_t InitCheck() const;
void SetSize(off_t size);
off_t Size() const { return fSize; }
status_t Remove(off_t offset, off_t count = 1);
status_t Set(off_t offset, addr_t address);
status_t Get(off_t offset, addr_t &address);
private:
struct block_entry;
status_t GetBlockEntry(off_t offset, block_entry **_entry);
hash_table *fHashTable;
off_t fSize;
};
#endif /* BLOCK_MAP_H */
+9
View File
@@ -0,0 +1,9 @@
SubDir OBOS_TOP src kernel core cache ;
KernelMergeObject kernel_cache.o :
block_cache.cpp
file_cache.cpp
vnode_store.cpp
: -fno-pic -Wno-unused
;
+839
View File
@@ -0,0 +1,839 @@
/*
* Copyright 2004-2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <KernelExport.h>
#include <fs_cache.h>
#include <cache.h>
#include <lock.h>
#include <util/kernel_cpp.h>
#include <util/DoublyLinkedList.h>
#include <util/khash.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
// ToDo: this is a naive implementation to test the API:
// 1) it does not have any useful memory management (just uses malloc/free)
// 2) block reading/writing is not at all optimized for speed, it will
// just read and write single blocks.
// 3) the locking could be improved; getting a block should not need to
// wait for blocks to be written
// 4) dirty blocks are only written back if asked for
// 5) blocks are never removed yet
#define TRACE_BLOCK_CACHE
#ifdef TRACE_BLOCK_CACHE
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define DEBUG_CHANGED
struct cache_transaction;
struct cached_block;
typedef DoublyLinkedListLink<cached_block> block_link;
struct cached_block {
cached_block *next; // next in hash
cached_block *transaction_next;
block_link previous_transaction_link;
off_t block_number;
void *data;
void *original;
#ifdef DEBUG_CHANGED
void *compare;
#endif
int32 ref_count;
int32 lock;
bool is_dirty;
cache_transaction *transaction;
cache_transaction *previous_transaction;
};
struct block_cache {
hash_table *hash;
benaphore lock;
int fd;
off_t max_blocks;
size_t block_size;
int32 next_transaction_id;
hash_table *transaction_hash;
};
typedef DoublyLinkedList<cached_block,
DoublyLinkedListMemberGetLink<cached_block,
&cached_block::previous_transaction_link> > block_list;
struct cache_transaction {
cache_transaction *next;
int32 id;
int32 num_blocks;
cached_block *first_block;
block_list blocks;
transaction_notification_hook notification_hook;
void *notification_data;
bool open;
};
struct cache {
hash_table *hash;
benaphore 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 BenaphoreLocker {
public:
BenaphoreLocker(int fd)
: fBenaphore(NULL)
{
if (fd < 0 || fd >= kNumCaches)
return;
fBenaphore = &sCaches[fd].lock;
benaphore_lock(fBenaphore);
}
BenaphoreLocker(block_cache *cache)
: fBenaphore(&cache->lock)
{
benaphore_lock(fBenaphore);
}
~BenaphoreLocker()
{
if (fBenaphore != NULL)
benaphore_unlock(fBenaphore);
}
status_t InitCheck()
{
return fBenaphore != NULL ? B_OK : B_ERROR;
}
private:
benaphore *fBenaphore;
};
static status_t write_cached_block(block_cache *cache, cached_block *block, bool deleteTransaction = true);
// private transaction functions
static int
transaction_compare(void *_transaction, const void *_id)
{
cache_transaction *transaction = (cache_transaction *)_transaction;
const int32 *id = (const int32 *)_id;
return transaction->id - *id;
}
static uint32
transaction_hash(void *_transaction, const void *_id, uint32 range)
{
cache_transaction *transaction = (cache_transaction *)_transaction;
const int32 *id = (const int32 *)_id;
if (transaction != NULL)
return transaction->id % range;
return *id % range;
}
static void
delete_transaction(block_cache *cache, cache_transaction *transaction)
{
hash_remove(cache->transaction_hash, transaction);
delete transaction;
}
static cache_transaction *
lookup_transaction(block_cache *cache, int32 id)
{
return (cache_transaction *)hash_lookup(cache->transaction_hash, &id);
}
// #pragma mark -
// private cached block functions
static int
cached_block_compare(void *_cacheEntry, const void *_block)
{
cached_block *cacheEntry = (cached_block *)_cacheEntry;
const off_t *block = (const off_t *)_block;
return cacheEntry->block_number - *block;
}
static uint32
cached_block_hash(void *_cacheEntry, const void *_block, uint32 range)
{
cached_block *cacheEntry = (cached_block *)_cacheEntry;
const off_t *block = (const off_t *)_block;
if (cacheEntry != NULL)
return cacheEntry->block_number % range;
return *block % range;
}
static void
free_cached_block(cached_block *block)
{
free(block->data);
free(block->original);
#ifdef DEBUG_CHANGED
free(block->compare);
#endif
free(block);
}
static cached_block *
new_cached_block(block_cache *cache, off_t blockNumber, bool cleared = false)
{
cached_block *block = (cached_block *)malloc(sizeof(cached_block));
if (block == NULL)
return NULL;
if (!cleared) {
block->data = malloc(cache->block_size);
if (block->data == NULL) {
free(block);
return NULL;
}
} else
block->data = NULL;
block->block_number = blockNumber;
block->lock = 0;
block->transaction_next = NULL;
block->transaction = block->previous_transaction = NULL;
block->original = NULL;
block->is_dirty = false;
#ifdef DEBUG_CHANGED
block->compare = NULL;
#endif
hash_insert(cache->hash, block);
return block;
}
#ifdef DEBUG_CHANGED
#define DUMPED_BLOCK_SIZE 16
void
dumpBlock(const char *buffer, int size, const char *prefix)
{
int i;
for (i = 0; i < size;) {
int start = i;
dprintf(prefix);
for (; i < start+DUMPED_BLOCK_SIZE; i++) {
if (!(i % 4))
dprintf(" ");
if (i >= size)
dprintf(" ");
else
dprintf("%02x", *(unsigned char *)(buffer + i));
}
dprintf(" ");
for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) {
if (i < size) {
char c = buffer[i];
if (c < 30)
dprintf(".");
else
dprintf("%c", c);
} else
break;
}
dprintf("\n");
}
}
#endif
static void
put_cached_block(block_cache *cache, cached_block *block)
{
#ifdef DEBUG_CHANGED
if (!block->is_dirty && block->compare != NULL && memcmp(block->data, block->compare, cache->block_size)) {
dprintf("new block:\n");
dumpBlock((const char *)block->data, 256, " ");
dprintf("unchanged block:\n");
dumpBlock((const char *)block->compare, 256, " ");
write_cached_block(cache, block);
panic("block_cache: supposed to be clean block was changed!\n");
free(block->compare);
block->compare = NULL;
}
#endif
block->lock--;
}
static void
put_cached_block(block_cache *cache, off_t blockNumber)
{
cached_block *block = (cached_block *)hash_lookup(cache->hash, &blockNumber);
if (block != NULL)
put_cached_block(cache, block);
}
static cached_block *
get_cached_block(block_cache *cache, off_t blockNumber, bool cleared = false)
{
cached_block *block = (cached_block *)hash_lookup(cache->hash, &blockNumber);
bool allocated = false;
if (block == NULL) {
// read block into cache
block = new_cached_block(cache, blockNumber, cleared);
if (block == NULL)
return NULL;
allocated = true;
}
if (!allocated && block->data == NULL && !cleared) {
// there is no block yet, but we need one
block->data = malloc(cache->block_size);
if (block->data == NULL)
return NULL;
allocated = true;
}
if (allocated && !cleared) {
int32 blockSize = cache->block_size;
if (read_pos(cache->fd, blockNumber * blockSize, block->data, blockSize) < blockSize) {
free_cached_block(block);
return NULL;
}
}
block->lock++;
return block;
}
static void *
get_writable_cached_block(block_cache *cache, off_t blockNumber, off_t base, off_t length,
int32 transactionID, bool cleared)
{
BenaphoreLocker locker(cache);
TRACE(("get_writable_cached_block(blockNumber = %Ld, transaction = %ld)\n", blockNumber, transactionID));
cached_block *block = get_cached_block(cache, blockNumber, cleared);
if (block == NULL)
return NULL;
// if there is no transaction support, we just return the current block
if (transactionID == -1) {
if (cleared && block->data == NULL) {
block->data = malloc(cache->block_size);
if (block->data == NULL) {
put_cached_block(cache, block);
return NULL;
}
}
if (cleared)
memset(block->data, 0, cache->block_size);
block->is_dirty = true;
// mark the block as dirty
return block->data;
}
// ToDo: note, even if we panic, we should probably put the cached block
// back before we return
if (block->transaction != NULL && block->transaction->id != transactionID) {
// ToDo: we have to wait here until the other transaction is done.
// Maybe we should even panic, since we can't prevent any deadlocks.
panic("block_cache_get_writable(): asked to get busy writable block\n");
return NULL;
}
if (block->transaction == NULL && transactionID != -1) {
// get new transaction
cache_transaction *transaction = lookup_transaction(cache, transactionID);
if (transaction == NULL) {
panic("block_cache_get_writable(): invalid transaction %ld!\n", transactionID);
return NULL;
}
if (!transaction->open) {
panic("block_cache_get_writable(): transaction already done!\n");
return NULL;
}
block->transaction = transaction;
// attach the block to the transaction block list
block->transaction_next = transaction->first_block;
transaction->first_block = block;
transaction->num_blocks++;
}
if (block->data != NULL && block->original == NULL) {
// we already have data, so we need to save it
block->original = malloc(cache->block_size);
if (block->original == NULL) {
put_cached_block(cache, block);
return NULL;
}
memcpy(block->original, block->data, cache->block_size);
}
if (block->data == NULL && cleared) {
// there is no data yet, we need a clean new block
block->data = malloc(cache->block_size);
if (block->data == NULL) {
put_cached_block(cache, block);
return NULL;
}
memset(block->data, 0, cache->block_size);
}
block->is_dirty = true;
return block->data;
}
static status_t
write_cached_block(block_cache *cache, cached_block *block, bool deleteTransaction)
{
cache_transaction *previous = block->previous_transaction;
int32 blockSize = cache->block_size;
void *data = previous && block->original ? block->original : block->data;
// we first need to write back changes from previous transactions
TRACE(("write_cached_block(block %Ld)\n", block->block_number));
ssize_t written = write_pos(cache->fd, block->block_number * blockSize, data, blockSize);
if (written < blockSize) {
dprintf("could not write back block %Ld (%s)\n", block->block_number, strerror(errno));
return B_IO_ERROR;
}
if (data == block->data)
block->is_dirty = false;
if (previous != NULL) {
previous->blocks.Remove(block);
block->previous_transaction = NULL;
// Has the previous transation been finished with that write?
if (--previous->num_blocks == 0) {
TRACE(("cache transaction %ld finished!\n", previous->id));
if (previous->notification_hook != NULL)
previous->notification_hook(previous->id, previous->notification_data);
if (deleteTransaction)
delete_transaction(cache, previous);
}
}
return B_OK;
}
// #pragma mark -
// Transactions
extern "C" int32
cache_start_transaction(void *_cache)
{
block_cache *cache = (block_cache *)_cache;
cache_transaction *transaction = new cache_transaction;
if (transaction == NULL)
return B_NO_MEMORY;
transaction->id = atomic_add(&cache->next_transaction_id, 1);
transaction->num_blocks = 0;
transaction->first_block = NULL;
transaction->notification_hook = NULL;
transaction->notification_data = NULL;
transaction->open = true;
TRACE(("cache_transaction_start(): id %ld started\n", transaction->id));
BenaphoreLocker locker(cache);
hash_insert(cache->transaction_hash, transaction);
return transaction->id;
}
extern "C" status_t
cache_sync_transaction(void *_cache, int32 id)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(cache);
status_t status = B_ENTRY_NOT_FOUND;
hash_iterator iterator;
hash_open(cache->transaction_hash, &iterator);
cache_transaction *transaction;
while ((transaction = (cache_transaction *)hash_next(cache->transaction_hash, &iterator)) != NULL) {
// ToDo: fix hash interface to make this easier
if (transaction->id <= id && !transaction->open) {
while (transaction->num_blocks > 0) {
status = write_cached_block(cache, transaction->blocks.Head(), false);
if (status != B_OK)
return status;
}
delete_transaction(cache, transaction);
hash_rewind(cache->transaction_hash, &iterator);
}
}
hash_close(cache->transaction_hash, &iterator, false);
return B_OK;
}
extern "C" status_t
cache_end_transaction(void *_cache, int32 id, transaction_notification_hook hook, void *data)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(cache);
TRACE(("cache_transaction_end(id = %ld)\n", id));
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL) {
panic("cache_transaction_end(): invalid transaction ID\n");
return B_BAD_VALUE;
}
transaction->notification_hook = hook;
transaction->notification_data = data;
// iterate through all blocks and free the unchanged original contents
cached_block *block = transaction->first_block, *next;
for (; block != NULL; block = next) {
next = block->transaction_next;
if (block->previous_transaction != NULL) {
// need to write back pending changes
write_cached_block(cache, block);
}
if (block->original != NULL) {
free(block->original);
block->original = NULL;
}
// move the block to the previous transaction list
transaction->blocks.Add(block);
block->previous_transaction = transaction;
block->transaction_next = NULL;
block->transaction = NULL;
}
transaction->open = false;
return B_OK;
}
extern "C" status_t
cache_abort_transaction(void *_cache, int32 id)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(cache);
return B_OK;
}
extern "C" int32
cache_detach_sub_transaction(void *_cache, int32 id)
{
return B_ERROR;
}
extern "C" status_t
cache_abort_sub_transaction(void *_cache, int32 id)
{
return B_ERROR;
}
extern "C" status_t
cache_start_sub_transaction(void *_cache, int32 id)
{
return B_ERROR;
}
extern "C" status_t
cache_next_block_in_transaction(void *_cache, int32 id, uint32 *_cookie, off_t *_blockNumber,
void **_data, void **_unchangedData)
{
cached_block *block = (cached_block *)*_cookie;
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(cache);
cache_transaction *transaction = lookup_transaction(cache, id);
if (transaction == NULL)
return B_BAD_VALUE;
if (block == NULL)
block = transaction->first_block;
else
block = block->transaction_next;
if (block == NULL)
return B_ENTRY_NOT_FOUND;
if (_blockNumber)
*_blockNumber = block->block_number;
if (_data)
*_data = block->data;
if (_unchangedData)
*_unchangedData = block->original;
*_cookie = (uint32)block;
return B_OK;
}
// #pragma mark -
// public interface
extern "C" void
block_cache_delete(void *_cache, bool allowWrites)
{
block_cache *cache = (block_cache *)_cache;
if (allowWrites)
block_cache_sync(cache);
// free all blocks
uint32 cookie = 0;
cached_block *block;
while ((block = (cached_block *)hash_remove_first(cache->hash, &cookie)) != NULL) {
free_cached_block(block);
}
// free all transactions (they will all be aborted)
cookie = 0;
cache_transaction *transaction;
while ((transaction = (cache_transaction *)hash_remove_first(cache->transaction_hash, &cookie)) != NULL) {
delete transaction;
}
hash_uninit(cache->hash);
hash_uninit(cache->transaction_hash);
benaphore_destroy(&cache->lock);
delete cache;
}
extern "C" void *
block_cache_create(int fd, off_t numBlocks, size_t blockSize)
{
block_cache *cache = new block_cache;
if (cache == NULL)
return NULL;
cache->hash = hash_init(32, 0, &cached_block_compare, &cached_block_hash);
if (cache->hash == NULL)
goto err1;
cache->transaction_hash = hash_init(16, 0, &transaction_compare, &transaction_hash);
if (cache->transaction_hash == NULL)
goto err2;
if (benaphore_init(&cache->lock, "block cache") < B_OK)
goto err3;
cache->fd = fd;
cache->max_blocks = numBlocks;
cache->block_size = blockSize;
cache->next_transaction_id = 1;
return cache;
err3:
hash_uninit(cache->transaction_hash);
err2:
hash_uninit(cache->hash);
err1:
delete cache;
return NULL;
}
extern "C" status_t
block_cache_sync(void *_cache)
{
block_cache *cache = (block_cache *)_cache;
// we will sync all dirty blocks to disk that have a completed
// transaction or no transaction only
BenaphoreLocker locker(cache);
hash_iterator iterator;
hash_open(cache->hash, &iterator);
cached_block *block;
while ((block = (cached_block *)hash_next(cache->hash, &iterator)) != NULL) {
if (block->previous_transaction != NULL
|| (block->transaction == NULL && block->is_dirty)) {
status_t status = write_cached_block(cache, block);
if (status != B_OK)
return status;
}
}
hash_close(cache->hash, &iterator, false);
return B_OK;
}
extern "C" status_t
block_cache_make_writable(void *_cache, off_t blockNumber, int32 transaction)
{
// ToDo: this can be done better!
void *block = block_cache_get_writable_etc(_cache, blockNumber, blockNumber, 1, transaction);
if (block != NULL) {
put_cached_block((block_cache *)_cache, blockNumber);
return B_OK;
}
return B_ERROR;
}
extern "C" void *
block_cache_get_writable_etc(void *_cache, off_t blockNumber, off_t base, off_t length,
int32 transaction)
{
TRACE(("block_cache_get_writable_etc(block = %Ld, transaction = %ld)\n", blockNumber, transaction));
return get_writable_cached_block((block_cache *)_cache, blockNumber,
base, length, transaction, false);
}
extern "C" void *
block_cache_get_writable(void *_cache, off_t blockNumber, int32 transaction)
{
return block_cache_get_writable_etc(_cache, blockNumber, blockNumber, 1, transaction);
}
extern "C" void *
block_cache_get_empty(void *_cache, off_t blockNumber, int32 transaction)
{
TRACE(("block_cache_get_empty(block = %Ld, transaction = %ld)\n", blockNumber, transaction));
return get_writable_cached_block((block_cache *)_cache, blockNumber,
blockNumber, 1, transaction, true);
}
extern "C" const void *
block_cache_get_etc(void *_cache, off_t blockNumber, off_t base, off_t length)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(cache);
cached_block *block = get_cached_block(cache, blockNumber);
if (block == NULL)
return NULL;
#ifdef DEBUG_CHANGED
if (block->compare == NULL)
block->compare = malloc(cache->block_size);
if (block->compare != NULL)
memcpy(block->compare, block->data, cache->block_size);
#endif
return block->data;
}
extern "C" const void *
block_cache_get(void *_cache, off_t blockNumber)
{
return block_cache_get_etc(_cache, blockNumber, blockNumber, 1);
}
extern "C" status_t
block_cache_set_dirty(void *_cache, off_t blockNumber, bool isDirty, int32 transaction)
{
// not yet implemented
// Note, you must only use this function on blocks that were acquired writable!
if (isDirty)
panic("block_cache_set_dirty(): not yet implemented that way!\n");
return B_OK;
}
extern "C" void
block_cache_put(void *_cache, off_t blockNumber)
{
block_cache *cache = (block_cache *)_cache;
BenaphoreLocker locker(cache);
put_cached_block(cache, blockNumber);
}
+870
View File
@@ -0,0 +1,870 @@
/*
* Copyright 2004-2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "vnode_store.h"
#include <KernelExport.h>
#include <fs_cache.h>
#include <util/kernel_cpp.h>
#include <file_cache.h>
#include <vfs.h>
#include <vm.h>
#include <vm_page.h>
#include <vm_cache.h>
#include <generic_syscall.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
//#define TRACE_FILE_CACHE
#ifdef TRACE_FILE_CACHE
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
// maximum number of iovecs per request
#define MAX_IO_VECS 64 // 256 kB
#define MAX_FILE_IO_VECS 32
struct file_cache_ref {
vm_cache_ref *cache;
void *vnode;
void *device;
void *cookie;
};
static struct cache_module_info *sCacheModule;
static void
add_to_iovec(iovec *vecs, int32 &index, int32 max, addr_t address, size_t size)
{
if (index > 0 && (addr_t)vecs[index - 1].iov_base + vecs[index - 1].iov_len == address) {
// the iovec can be combined with the previous one
vecs[index - 1].iov_len += size;
return;
}
// we need to start a new iovec
vecs[index].iov_base = (void *)address;
vecs[index].iov_len = size;
index++;
}
static status_t
pages_io(file_cache_ref *ref, off_t offset, const iovec *vecs, size_t count,
size_t *_numBytes, bool doWrite)
{
TRACE(("pages_io: ref = %p, offset = %Ld, size = %lu, %s\n", ref, offset,
*_numBytes, doWrite ? "write" : "read"));
// translate the iovecs into direct device accesses
file_io_vec fileVecs[MAX_FILE_IO_VECS];
size_t fileVecCount = MAX_FILE_IO_VECS;
size_t numBytes = *_numBytes;
// ToDo: these must be cacheable (must for the swap file, great for all other)
status_t status = vfs_get_file_map(ref->vnode, offset, numBytes, fileVecs, &fileVecCount);
if (status < B_OK)
return status;
// ToDo: handle array overflow gracefully!
#ifdef TRACE_FILE_CACHE
dprintf("got %lu file vecs:\n", fileVecCount);
for (size_t i = 0; i < fileVecCount; i++)
dprintf("[%lu] offset = %Ld, size = %Ld\n", i, fileVecs[i].offset, fileVecs[i].length);
#endif
uint32 fileVecIndex;
size_t size;
if (!doWrite) {
// now directly read the data from the device
// the first file_io_vec can be read directly
size = fileVecs[0].length;
if (size > numBytes)
size = numBytes;
status = vfs_read_pages(ref->device, ref->cookie, fileVecs[0].offset, vecs, count, &size);
if (status < B_OK)
return status;
// ToDo: this is a work-around for buggy device drivers!
// When our own drivers honour the length, we can:
// a) also use this direct I/O for writes (otherwise, it would overwrite precious data)
// b) panic if the term below is true (at least for writes)
if (size > fileVecs[0].length) {
dprintf("warning: device driver %p doesn't respect total length in read_pages() call!\n", ref->device);
size = fileVecs[0].length;
}
ASSERT(size <= fileVecs[0].length);
// If the file portion was contiguous, we're already done now
if (size == numBytes)
return B_OK;
// if we reached the end of the file, we can return as well
if (size != fileVecs[0].length) {
*_numBytes = size;
return B_OK;
}
fileVecIndex = 1;
} else {
fileVecIndex = 0;
size = 0;
}
// Too bad, let's process the rest of the file_io_vecs
size_t totalSize = size;
// first, find out where we have to continue in our iovecs
uint32 i = 0;
for (; i < count; i++) {
if (size <= vecs[i].iov_len)
break;
size -= vecs[i].iov_len;
}
size_t vecOffset = size;
for (; fileVecIndex < fileVecCount; fileVecIndex++) {
file_io_vec &fileVec = fileVecs[fileVecIndex];
iovec tempVecs[8];
uint32 tempCount = 1;
tempVecs[0].iov_base = (void *)((addr_t)vecs[i].iov_base + vecOffset);
size = min_c(vecs[i].iov_len - vecOffset, fileVec.length);
tempVecs[0].iov_len = size;
TRACE(("fill vec %ld, offset = %lu, size = %lu\n", i, vecOffset, size));
if (size >= fileVec.length)
vecOffset += size;
else
vecOffset = 0;
while (size < fileVec.length && ++i < count) {
tempVecs[tempCount].iov_base = vecs[i].iov_base;
tempCount++;
// is this iovec larger than the file_io_vec?
if (vecs[i].iov_len + size > fileVec.length) {
size += tempVecs[tempCount].iov_len = vecOffset = fileVec.length - size;
break;
}
size += tempVecs[tempCount].iov_len = vecs[i].iov_len;
}
size_t bytes = size;
if (doWrite)
status = vfs_write_pages(ref->device, ref->cookie, fileVec.offset, tempVecs, tempCount, &bytes);
else
status = vfs_read_pages(ref->device, ref->cookie, fileVec.offset, tempVecs, tempCount, &bytes);
if (status < B_OK)
return status;
totalSize += size;
if (size != bytes) {
// there are no more bytes, let's bail out
*_numBytes = totalSize;
return B_OK;
}
}
return B_OK;
}
/** This function is called by read_into_cache() (and from there only) - it
* can only handle a certain amount of bytes, and read_into_cache() makes
* sure that it matches that criterion.
*/
static inline status_t
read_chunk_into_cache(file_cache_ref *ref, off_t offset, size_t size,
int32 pageOffset, addr_t buffer, size_t bufferSize)
{
TRACE(("read_chunk(offset = %Ld, size = %lu, pageOffset = %ld, buffer = %#lx, bufferSize = %lu\n",
offset, size, pageOffset, buffer, bufferSize));
vm_cache_ref *cache = ref->cache;
iovec vecs[MAX_IO_VECS];
int32 vecCount = 0;
vm_page *pages[MAX_IO_VECS];
int32 pageIndex = 0;
// allocate pages for the cache and mark them busy
for (size_t pos = 0; pos < size; pos += B_PAGE_SIZE) {
vm_page *page = pages[pageIndex++] = vm_page_allocate_page(PAGE_STATE_FREE);
page->state = PAGE_STATE_BUSY;
vm_cache_insert_page(cache, page, offset + pos);
addr_t virtualAddress;
vm_get_physical_page(page->ppn * B_PAGE_SIZE, &virtualAddress, PHYSICAL_PAGE_CAN_WAIT);
add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, B_PAGE_SIZE);
// ToDo: check if the array is large enough!
}
mutex_unlock(&cache->lock);
// read file into reserved pages
status_t status = pages_io(ref, offset, vecs, vecCount, &size, false);
if (status < B_OK) {
// ToDo: remove allocated pages...
panic("file_cache: remove allocated pages! read pages failed: %s\n", strerror(status));
mutex_lock(&cache->lock);
return status;
}
// copy the pages and unmap them again
for (int32 i = 0; i < vecCount; i++) {
addr_t base = (addr_t)vecs[i].iov_base;
size_t size = vecs[i].iov_len;
// copy to user buffer if necessary
if (bufferSize != 0) {
size_t bytes = min_c(bufferSize, size - pageOffset);
user_memcpy((void *)buffer, (void *)(base + pageOffset), bytes);
buffer += bytes;
bufferSize -= bytes;
pageOffset = 0;
}
for (size_t pos = 0; pos < size; pos += B_PAGE_SIZE, base += B_PAGE_SIZE)
vm_put_physical_page(base);
}
mutex_lock(&cache->lock);
// make the pages accessible in the cache
for (int32 i = pageIndex; i-- > 0;)
pages[i]->state = PAGE_STATE_ACTIVE;
return B_OK;
}
/** This function reads \a size bytes directly from the file into the cache.
* If \a bufferSize does not equal zero, \a bufferSize bytes from the data
* read in are also copied to the provided \a buffer.
* This function always allocates all pages; it is the responsibility of the
* calling function to only ask for yet uncached ranges.
* The cache_ref lock must be hold when calling this function.
*/
static status_t
read_into_cache(file_cache_ref *ref, off_t offset, size_t size, addr_t buffer, size_t bufferSize)
{
TRACE(("read_from_cache: ref = %p, offset = %Ld, size = %lu, buffer = %p, bufferSize = %lu\n",
ref, offset, size, (void *)buffer, bufferSize));
// make sure "offset" is page aligned - but also remember the page offset
int32 pageOffset = offset & (B_PAGE_SIZE - 1);
size = PAGE_ALIGN(size + pageOffset);
offset -= pageOffset;
while (true) {
size_t chunkSize = size;
if (chunkSize > (MAX_IO_VECS * B_PAGE_SIZE))
chunkSize = MAX_IO_VECS * B_PAGE_SIZE;
status_t status = read_chunk_into_cache(ref, offset, chunkSize, pageOffset, buffer, bufferSize);
if (status != B_OK)
return status;
if ((size -= chunkSize) == 0)
return B_OK;
if (chunkSize >= bufferSize) {
bufferSize = 0;
buffer = NULL;
} else {
bufferSize -= chunkSize - pageOffset;
buffer += chunkSize - pageOffset;
}
offset += chunkSize;
pageOffset = 0;
}
return B_OK;
}
/** Like read_chunk_into_cache() but writes data into the cache */
static inline status_t
write_chunk_to_cache(file_cache_ref *ref, off_t offset, size_t size,
int32 pageOffset, addr_t buffer, size_t bufferSize)
{
iovec vecs[MAX_IO_VECS];
int32 vecCount = 0;
vm_page *pages[MAX_IO_VECS];
int32 pageIndex = 0;
// allocate pages for the cache and mark them busy
for (size_t pos = 0; pos < size; pos += B_PAGE_SIZE) {
vm_page *page = pages[pageIndex++] = vm_page_allocate_page(PAGE_STATE_FREE);
page->state = PAGE_STATE_BUSY;
vm_cache_insert_page(ref->cache, page, offset + pos);
addr_t virtualAddress;
vm_get_physical_page(page->ppn * B_PAGE_SIZE, &virtualAddress, PHYSICAL_PAGE_CAN_WAIT);
add_to_iovec(vecs, vecCount, MAX_IO_VECS, virtualAddress, B_PAGE_SIZE);
// ToDo: check if the array is large enough!
size_t bytes = min_c(bufferSize, size_t(B_PAGE_SIZE - pageOffset));
if (bytes != B_PAGE_SIZE) {
// This is only a partial write, so we have to read the rest of the page
// from the file to have consistent data in the cache
size_t bytesRead = B_PAGE_SIZE;
iovec readVec = { (void *)virtualAddress, B_PAGE_SIZE };
// ToDo: when calling pages_io(), unlocking the cache_ref would be
// a great idea. But we can't do this in this loop, so the whole
// thing should be changed so that pages_io() and the copy stuff
// below can be called without holding the lock
pages_io(ref, offset + pos, &readVec, 1, &bytesRead, false);
// ToDo: handle errors!
}
// copy data from user buffer if necessary
if (bufferSize != 0) {
user_memcpy((void *)(virtualAddress + pageOffset), (void *)buffer, bytes);
buffer += bytes;
bufferSize -= bytes;
vm_page_set_state(page, PAGE_STATE_MODIFIED);
}
pageOffset = 0;
}
// ToDo: we only have to write the pages back immediately if write-back mode
// is disabled, which is not possible right now
#if 0
// write cached pages back to the file if we were asked to do that
status_t status = readwrite_pages(ref, offset, vecs, vecCount, &size, true);
if (status < B_OK) {
// ToDo: remove allocated pages...
panic("file_cache: remove allocated pages! write pages failed: %s\n", strerror(status));
return status;
}
#endif
// unmap the pages again
for (int32 i = 0; i < vecCount; i++) {
addr_t base = (addr_t)vecs[i].iov_base;
size_t size = vecs[i].iov_len;
for (size_t pos = 0; pos < size; pos += B_PAGE_SIZE, base += B_PAGE_SIZE)
vm_put_physical_page(base);
}
// make the pages accessible in the cache
for (int32 i = pageIndex; i-- > 0;) {
if (pages[i]->state == PAGE_STATE_BUSY)
pages[i]->state = PAGE_STATE_ACTIVE;
}
return B_OK;
}
/** Like read_into_cache() but writes data into the cache. To preserve data consistency,
* it might also read pages into the cache, though, if only a partial page gets written.
* The cache_ref lock must be hold when calling this function.
*/
static status_t
write_to_cache(file_cache_ref *ref, off_t offset, size_t size, addr_t buffer, size_t bufferSize)
{
TRACE(("write_to_cache: ref = %p, offset = %Ld, size = %lu, buffer = %p, bufferSize = %lu\n",
ref, offset, size, (void *)buffer, bufferSize));
// make sure "offset" is page aligned - but also remember the page offset
int32 pageOffset = offset & (B_PAGE_SIZE - 1);
size = PAGE_ALIGN(size + pageOffset);
offset -= pageOffset;
while (true) {
size_t chunkSize = size;
if (chunkSize > (MAX_IO_VECS * B_PAGE_SIZE))
chunkSize = MAX_IO_VECS * B_PAGE_SIZE;
status_t status = write_chunk_to_cache(ref, offset, chunkSize, pageOffset, buffer, bufferSize);
if (status != B_OK)
return status;
if ((size -= chunkSize) == 0)
return B_OK;
if (chunkSize >= bufferSize) {
bufferSize = 0;
buffer = NULL;
} else {
bufferSize -= chunkSize - pageOffset;
buffer += chunkSize - pageOffset;
}
offset += chunkSize;
pageOffset = 0;
}
return B_OK;
}
static status_t
cache_io(void *_cacheRef, off_t offset, addr_t buffer, size_t *_size, bool doWrite)
{
if (_cacheRef == NULL)
panic("cache_io() called with NULL ref!\n");
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
vm_cache_ref *cache = ref->cache;
off_t fileSize = cache->cache->virtual_size;
TRACE(("cache_io(ref = %p, offset = %Ld, buffer = %p, size = %lu, %s)\n",
ref, offset, (void *)buffer, *_size, doWrite ? "write" : "read"));
// out of bounds access?
if (offset >= fileSize || offset < 0) {
*_size = 0;
return B_OK;
}
int32 pageOffset = offset & (B_PAGE_SIZE - 1);
size_t size = *_size;
offset -= pageOffset;
if (offset + pageOffset + size > fileSize) {
// adapt size to be within the file's offsets
size = fileSize - pageOffset - offset;
*_size = size;
}
// "offset" and "lastOffset" are always aligned to B_PAGE_SIZE,
// the "last*" variables always point to the end of the last
// satisfied request part
size_t bytesLeft = size, lastLeft = size;
int32 lastPageOffset = pageOffset;
addr_t lastBuffer = buffer;
off_t lastOffset = offset;
mutex_lock(&cache->lock);
for (; bytesLeft > 0; offset += B_PAGE_SIZE) {
// check if this page is already in memory
addr_t virtualAddress;
restart:
vm_page *page = vm_cache_lookup_page(cache, offset);
if (page != NULL && page->state == PAGE_STATE_BUSY) {
// ToDo: don't wait forever!
mutex_unlock(&cache->lock);
snooze(20000);
mutex_lock(&cache->lock);
goto restart;
}
size_t bytesInPage = min_c(size_t(B_PAGE_SIZE - pageOffset), bytesLeft);
TRACE(("lookup page from offset %Ld: %p, size = %lu, pageOffset = %lu\n", offset, page, bytesLeft, pageOffset));
if (page != NULL
&& vm_get_physical_page(page->ppn * B_PAGE_SIZE,
&virtualAddress, PHYSICAL_PAGE_CAN_WAIT) == B_OK) {
// it is, so let's satisfy the first part of the request, if we have to
if (lastBuffer != buffer) {
size_t requestSize = buffer - lastBuffer;
status_t status;
if (doWrite) {
status = write_to_cache(ref, lastOffset + lastPageOffset,
requestSize, lastBuffer, requestSize);
} else {
status = read_into_cache(ref, lastOffset + lastPageOffset,
requestSize, lastBuffer, requestSize);
}
if (status != B_OK) {
vm_put_physical_page(virtualAddress);
mutex_unlock(&cache->lock);
return B_IO_ERROR;
}
}
// and copy the contents of the page already in memory
if (doWrite)
user_memcpy((void *)(virtualAddress + pageOffset), (void *)buffer, bytesInPage);
else
user_memcpy((void *)buffer, (void *)(virtualAddress + pageOffset), bytesInPage);
vm_put_physical_page(virtualAddress);
if (bytesLeft <= bytesInPage) {
// we've read the last page, so we're done!
mutex_unlock(&cache->lock);
return B_OK;
}
// prepare a potential gap request
lastBuffer = buffer + bytesInPage;
lastLeft = bytesLeft - bytesInPage;
lastOffset = offset + B_PAGE_SIZE;
lastPageOffset = 0;
}
if (bytesLeft <= bytesInPage)
break;
buffer += bytesInPage;
bytesLeft -= bytesInPage;
pageOffset = 0;
}
// fill the last remaining bytes of the request (either write or read)
status_t status;
if (doWrite)
status = write_to_cache(ref, lastOffset + lastPageOffset, lastLeft, lastBuffer, lastLeft);
else
status = read_into_cache(ref, lastOffset + lastPageOffset, lastLeft, lastBuffer, lastLeft);
mutex_unlock(&cache->lock);
return status;
}
static status_t
file_cache_control(const char *subsystem, uint32 function, void *buffer, size_t bufferSize)
{
switch (function) {
case CACHE_CLEAR:
// ToDo: clear the cache
dprintf("cache_control: clear cache!\n");
break;
case CACHE_SET_MODULE:
{
cache_module_info *module = sCacheModule;
// unset previous module
if (sCacheModule != NULL) {
sCacheModule = NULL;
snooze(100000); // 0.1 secs
put_module(module->info.name);
}
// get new module, if any
if (buffer == NULL)
break;
char name[B_FILE_NAME_LENGTH];
if (!IS_USER_ADDRESS(buffer)
|| user_strlcpy(name, (char *)buffer, B_FILE_NAME_LENGTH) < B_OK)
return B_BAD_ADDRESS;
if (strncmp(name, CACHE_MODULES_NAME, strlen(CACHE_MODULES_NAME)))
return B_BAD_VALUE;
dprintf("cache_control: set module %s!\n", name);
if (get_module(name, (module_info **)&module) == B_OK)
sCacheModule = module;
break;
}
}
return B_OK;
}
// #pragma mark -
// kernel public API
extern "C" void
cache_prefetch(mount_id mountID, vnode_id vnodeID)
{
vm_cache_ref *cache;
void *vnode;
// ToDo: schedule prefetch
// ToDo: maybe get 1) access type (random/sequential), 2) file vecs which blocks to prefetch
// for now, we just prefetch the first 64 kB
TRACE(("cache_prefetch(vnode %ld:%Ld)\n", mountID, vnodeID));
// get the vnode for the object, this also grabs a ref to it
if (vfs_get_vnode(mountID, vnodeID, &vnode) != B_OK)
return;
if (vfs_get_vnode_cache(vnode, &cache) != B_OK) {
vfs_vnode_release_ref(vnode);
return;
}
file_cache_ref *ref = (struct file_cache_ref *)((vnode_store *)cache->cache->store)->file_cache_ref;
off_t fileSize = cache->cache->virtual_size;
size_t size = 65536;
if (size > fileSize)
size = fileSize;
size_t bytesLeft = size, lastLeft = size;
off_t lastOffset = 0;
size_t lastSize = 0;
mutex_lock(&cache->lock);
for (off_t offset = 0; bytesLeft > 0; offset += B_PAGE_SIZE) {
// check if this page is already in memory
addr_t virtualAddress;
restart:
vm_page *page = vm_cache_lookup_page(cache, offset);
if (page != NULL) {
// it is, so let's satisfy in the first part of the request
if (lastOffset < offset) {
size_t requestSize = offset - lastOffset;
read_into_cache(ref, lastOffset, requestSize, NULL, 0);
}
if (bytesLeft <= B_PAGE_SIZE) {
// we've read the last page, so we're done!
goto out;
}
// prepare a potential gap request
lastOffset = offset + B_PAGE_SIZE;
lastLeft = bytesLeft - B_PAGE_SIZE;
}
if (bytesLeft <= B_PAGE_SIZE)
break;
bytesLeft -= B_PAGE_SIZE;
}
// read in the last part
read_into_cache(ref, lastOffset, lastLeft, NULL, 0);
out:
mutex_unlock(&cache->lock);
vfs_vnode_release_ref(vnode);
}
extern "C" void
cache_node_opened(void *vnode, int32 fdType, vm_cache_ref *cache, mount_id mountID,
vnode_id parentID, vnode_id vnodeID, const char *name)
{
if (sCacheModule == NULL)
return;
off_t size = -1;
if (cache != NULL) {
file_cache_ref *ref = (file_cache_ref *)((vnode_store *)cache->cache->store)->file_cache_ref;
if (ref != NULL)
size = ref->cache->cache->virtual_size;
}
sCacheModule->node_opened(vnode, fdType, mountID, parentID, vnodeID, name, size);
}
extern "C" void
cache_node_closed(void *vnode, int32 fdType, vm_cache_ref *cache,
mount_id mountID, vnode_id vnodeID)
{
if (sCacheModule == NULL)
return;
int32 accessType = 0;
if (cache != NULL) {
// ToDo: set accessType
}
sCacheModule->node_closed(vnode, fdType, mountID, vnodeID, accessType);
}
extern "C" void
cache_node_launched(size_t argCount, char * const *args)
{
if (sCacheModule == NULL)
return;
sCacheModule->node_launched(argCount, args);
}
extern "C" status_t
file_cache_init(void)
{
// ToDo: get cache module out of driver settings
register_generic_syscall(CACHE_SYSCALLS, file_cache_control, 1, 0);
return B_OK;
}
// #pragma mark -
// public FS API
extern "C" void *
file_cache_create(mount_id mountID, vnode_id vnodeID, off_t size, int fd)
{
TRACE(("file_cache_create(mountID = %ld, vnodeID = %Ld, size = %Ld, fd = %d)\n", mountID, vnodeID, size, fd));
file_cache_ref *ref = new file_cache_ref;
if (ref == NULL)
return NULL;
// get the vnode of the underlying device
if (vfs_get_vnode_from_fd(fd, true, &ref->device) != B_OK)
goto err1;
// we also need the cookie of the underlying device to properly access it
if (vfs_get_cookie_from_fd(fd, &ref->cookie) != B_OK)
goto err2;
// get the vnode for the object (note, this does not grab a reference to the node)
if (vfs_lookup_vnode(mountID, vnodeID, &ref->vnode) != B_OK)
goto err2;
if (vfs_get_vnode_cache(ref->vnode, &ref->cache) != B_OK)
goto err3;
ref->cache->cache->virtual_size = size;
((vnode_store *)ref->cache->cache->store)->file_cache_ref = ref;
return ref;
err3:
vfs_vnode_release_ref(ref->vnode);
err2:
vfs_vnode_release_ref(ref->device);
err1:
delete ref;
return NULL;
}
extern "C" void
file_cache_delete(void *_cacheRef)
{
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
if (ref == NULL)
return;
TRACE(("file_cache_delete(ref = %p)\n", ref));
vfs_vnode_release_ref(ref->device);
delete ref;
}
extern "C" status_t
file_cache_set_size(void *_cacheRef, off_t size)
{
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
TRACE(("file_cache_set_size(ref = %p, size = %Ld)\n", ref, size));
if (ref == NULL)
return B_OK;
mutex_lock(&ref->cache->lock);
status_t status = vm_cache_resize(ref->cache, size);
mutex_unlock(&ref->cache->lock);
return status;
}
extern "C" status_t
file_cache_sync(void *_cacheRef)
{
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
if (ref == NULL)
return B_BAD_VALUE;
return vm_cache_write_modified(ref->cache);
}
extern "C" status_t
file_cache_read_pages(void *_cacheRef, off_t offset, const iovec *vecs, size_t count, size_t *_numBytes)
{
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
return pages_io(ref, offset, vecs, count, _numBytes, false);
}
extern "C" status_t
file_cache_write_pages(void *_cacheRef, off_t offset, const iovec *vecs, size_t count, size_t *_numBytes)
{
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
status_t status = pages_io(ref, offset, vecs, count, _numBytes, true);
TRACE(("file_cache_write_pages(ref = %p, offset = %Ld, vecs = %p, count = %lu, bytes = %lu) = %ld\n",
ref, offset, vecs, count, *_numBytes, status));
return status;
}
extern "C" status_t
file_cache_read(void *_cacheRef, off_t offset, void *bufferBase, size_t *_size)
{
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
TRACE(("file_cache_read(ref = %p, offset = %Ld, buffer = %p, size = %lu)\n",
ref, offset, bufferBase, *_size));
return cache_io(ref, offset, (addr_t)bufferBase, _size, false);
}
extern "C" status_t
file_cache_write(void *_cacheRef, off_t offset, const void *buffer, size_t *_size)
{
file_cache_ref *ref = (file_cache_ref *)_cacheRef;
status_t status = cache_io(ref, offset, (addr_t)const_cast<void *>(buffer), _size, true);
TRACE(("file_cache_write(ref = %p, offset = %Ld, buffer = %p, size = %lu) = %ld\n",
ref, offset, buffer, *_size, status));
return status;
}
+129
View File
@@ -0,0 +1,129 @@
/*
* Copyright 2004, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "vnode_store.h"
#include <file_cache.h>
#include <vfs.h>
#include <stdlib.h>
#include <string.h>
static void
store_destroy(struct vm_store *store)
{
free(store);
}
static status_t
store_commit(struct vm_store *_store, off_t size)
{
vnode_store *store = (vnode_store *)_store;
store->vm.committed_size = size;
return B_OK;
}
static bool
store_has_page(struct vm_store *_store, off_t offset)
{
// We always pretend to have the page - even if it's beyond the size of
// the file. The read function will only cut down the size of the read,
// it won't fail because of that.
return true;
}
static status_t
store_read(struct vm_store *_store, off_t offset, const iovec *vecs, size_t count, size_t *_numBytes)
{
vnode_store *store = (vnode_store *)_store;
size_t bytesUntouched = *_numBytes;
status_t status = vfs_read_pages(store->vnode, NULL, offset, vecs, count, _numBytes);
bytesUntouched -= *_numBytes;
// if the request could be filled completely, or an error occured, we're done here
if (status < B_OK || bytesUntouched == 0)
return status;
// Clear out any leftovers that were not touched by the above read - we're
// doing this here so that not every file system/device has to implement
// this
for (int32 i = count; i-- > 0 && bytesUntouched != 0;) {
size_t length = min_c(bytesUntouched, vecs[i].iov_len);
// ToDo: will have to map the pages in later (when we switch to physical pages)
memset((void *)((addr_t)vecs[i].iov_base + vecs[i].iov_len - length), 0, length);
bytesUntouched -= length;
}
return B_OK;
}
static status_t
store_write(struct vm_store *_store, off_t offset, const iovec *vecs, size_t count, size_t *_numBytes)
{
vnode_store *store = (vnode_store *)_store;
return vfs_write_pages(store->vnode, NULL, offset, vecs, count, _numBytes);
}
static void
store_acquire_ref(struct vm_store *_store)
{
vnode_store *store = (vnode_store *)_store;
vfs_vnode_acquire_ref(store->vnode);
}
static void
store_release_ref(struct vm_store *_store)
{
vnode_store *store = (vnode_store *)_store;
vfs_vnode_release_ref(store->vnode);
}
static vm_store_ops sStoreOps = {
&store_destroy,
&store_commit,
&store_has_page,
&store_read,
&store_write,
NULL, /* fault */
&store_acquire_ref,
&store_release_ref
};
// #pragma mark -
extern "C" vm_store *
vm_create_vnode_store(void *vnode)
{
vnode_store *store = (vnode_store *)malloc(sizeof(struct vnode_store));
if (store == NULL) {
vfs_vnode_release_ref(vnode);
return NULL;
}
store->vm.ops = &sStoreOps;
store->vm.cache = NULL;
store->vm.committed_size = 0;
store->vnode = vnode;
store->file_cache_ref = NULL;
return &store->vm;
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright 2004, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef VNODE_STORE_H
#define VNODE_STORE_H
#include <vm.h>
struct vnode_store {
vm_store vm;
void *vnode;
void *file_cache_ref;
};
#endif /* VNODE_STORE_H */