mirror of
https://github.com/ksherlock/profuse.git
synced 2025-01-10 07:30:09 +00:00
new branch to integrate BlockDevice, BlockCache
git-svn-id: https://profuse.googlecode.com/svn/branches/profuse_interim@331 aa027e90-d47c-11dd-86d7-074df07e0730
This commit is contained in:
parent
e5f935e435
commit
14e9b43f32
73
Cache/BlockCache.cpp
Normal file
73
Cache/BlockCache.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include <Cache/BlockCache.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <ProFUSE/Exception.h>
|
||||
#include <ProFUSE/auto.h>
|
||||
|
||||
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
using ProFUSE::Exception;
|
||||
using ProFUSE::POSIXException;
|
||||
|
||||
|
||||
BlockCache::BlockCache(BlockDevice *device)
|
||||
{
|
||||
_device = device;
|
||||
_blocks = device->blocks();
|
||||
_readOnly = device->readOnly();
|
||||
}
|
||||
|
||||
BlockCache::~BlockCache()
|
||||
{
|
||||
delete _device;
|
||||
}
|
||||
|
||||
void BlockCache::write(unsigned block, const void *bp)
|
||||
{
|
||||
void *address = acquire(block);
|
||||
std::memcpy(address, bp, 512);
|
||||
release(block, true);
|
||||
}
|
||||
|
||||
void BlockCache::read(unsigned block, void *bp)
|
||||
{
|
||||
void *address = acquire(block);
|
||||
std::memcpy(bp, address, 512);
|
||||
release(block, false);
|
||||
}
|
||||
|
||||
|
||||
BlockCache *BlockCache::Create(BlockDevice *device)
|
||||
{
|
||||
if (!device) return NULL;
|
||||
|
||||
return device->createBlockCache();
|
||||
}
|
||||
|
||||
|
||||
void BlockCache::zeroBlock(unsigned block)
|
||||
{
|
||||
/*
|
||||
void *address = acquire(block);
|
||||
std::memset(address, 0, 512);
|
||||
release(block, true);
|
||||
*/
|
||||
|
||||
uint8_t buffer[512];
|
||||
|
||||
std::memset(buffer, 0, 512);
|
||||
write(block, buffer);
|
||||
}
|
61
Cache/BlockCache.h
Normal file
61
Cache/BlockCache.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef __BLOCKCACHE_H__
|
||||
#define __BLOCKCACHE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
class MappedFile;
|
||||
|
||||
namespace Device {
|
||||
|
||||
class BlockDevice;
|
||||
|
||||
enum BlockReleaseFlags {
|
||||
kBlockDirty = 1,
|
||||
kBlockCommitNow = 2,
|
||||
kBlockReuse = 3
|
||||
};
|
||||
|
||||
class BlockCache {
|
||||
public:
|
||||
|
||||
static BlockCache *Create(BlockDevice *device);
|
||||
|
||||
virtual ~BlockCache();
|
||||
|
||||
bool readOnly() { return _readOnly; }
|
||||
unsigned blocks() { return _blocks; }
|
||||
BlockDevice *device() { return _device; }
|
||||
|
||||
|
||||
virtual void sync() = 0;
|
||||
virtual void write(unsigned block, const void *bp);
|
||||
virtual void read(unsigned block, void *bp);
|
||||
|
||||
virtual void *acquire(unsigned block) = 0;
|
||||
virtual void release(unsigned block, int flags) = 0 ;
|
||||
virtual void markDirty(unsigned block) = 0;
|
||||
|
||||
|
||||
virtual void zeroBlock(unsigned block);
|
||||
|
||||
void release(unsigned block) { release(block, 0); }
|
||||
void release(unsigned block, bool dirty)
|
||||
{
|
||||
release(block, dirty ? kBlockDirty : 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
BlockCache(BlockDevice *device);
|
||||
|
||||
BlockDevice *_device;
|
||||
|
||||
private:
|
||||
unsigned _blocks;
|
||||
bool _readOnly;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
375
Cache/ConcreteBlockCache.cpp
Normal file
375
Cache/ConcreteBlockCache.cpp
Normal file
@ -0,0 +1,375 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Device/BlockDevice.h>
|
||||
#include <Cache/ConcreteBlockCache.h>
|
||||
|
||||
#include <ProFUSE/Exception.h>
|
||||
#include <ProFUSE/auto.h>
|
||||
|
||||
|
||||
/*
|
||||
* Note -- everything is assumed to be single-threaded.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The primary purpose of the block cache is not as a cache
|
||||
* (the OS probably does a decent job of that) but rather to
|
||||
* simplify read/writes. Blocks will always be accessed by
|
||||
* pointer, so any updates will be shared.
|
||||
* For memory mapped prodos-order files, MappedBlockCache just
|
||||
* returns a pointer to the memory.
|
||||
* For dos-order, nibblized, or raw devices, ConcreteBlockCache
|
||||
* uses an approach similar to minix (although the buffer pool will
|
||||
* expand if needed).
|
||||
*
|
||||
* _buffers is a vector of all buffers and only exists to make
|
||||
* freeing them easier.
|
||||
* _hashTable is a simple hashtable of loaded blocks.
|
||||
* _first and _last are a double-linked list of unused blocks, stored
|
||||
* in lru order.
|
||||
*
|
||||
* The Entry struct contains the buffer, the block, a dirty flag, and an in-use
|
||||
* count as well as pointer for the hashtable and lru list.
|
||||
* When a block is loaded, it is stored in the _hashTable. It remains in the
|
||||
* hash table when the in-use count goes to 0 (it will also be added to the
|
||||
* end of the lru list).
|
||||
*
|
||||
* dirty buffers are only written to disk in 3 scenarios:
|
||||
* a) sync() is called
|
||||
* b) the cache is deleted
|
||||
* c) a buffer is re-used from the lru list.
|
||||
*/
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
using ProFUSE::Exception;
|
||||
using ProFUSE::POSIXException;
|
||||
|
||||
|
||||
//typedef std::vector<ConcreteBlockCache::Entry *>::iterator EntryIter;
|
||||
|
||||
|
||||
|
||||
ConcreteBlockCache::ConcreteBlockCache(BlockDevice *device, unsigned size) :
|
||||
BlockCache(device)
|
||||
{
|
||||
if (size < 16) size = 16;
|
||||
|
||||
std::memset(_hashTable, 0, sizeof(Entry *) * HashTableSize);
|
||||
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
Entry *e = new Entry;
|
||||
|
||||
std::memset(e, 0, sizeof(Entry));
|
||||
_buffers.push_back(e);
|
||||
}
|
||||
_first = _last = NULL;
|
||||
|
||||
_first = _buffers.front();
|
||||
_last = _buffers.back();
|
||||
// form a chain....
|
||||
for (unsigned i = 0; i < size; ++i)
|
||||
{
|
||||
Entry *e = _buffers[i];
|
||||
|
||||
if (i > 0) e->prev = _buffers[i - 1];
|
||||
|
||||
if (i < size - 1) e->next = _buffers[i + 1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ConcreteBlockCache::~ConcreteBlockCache()
|
||||
{
|
||||
EntryIter iter;
|
||||
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
|
||||
{
|
||||
Entry *e = *iter;
|
||||
|
||||
if (e->dirty)
|
||||
{
|
||||
_device->write(e->block, e->buffer);
|
||||
}
|
||||
|
||||
delete e;
|
||||
}
|
||||
_device->sync();
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::sync()
|
||||
{
|
||||
EntryIter iter;
|
||||
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
|
||||
{
|
||||
Entry *e = *iter;
|
||||
|
||||
if (e->dirty)
|
||||
{
|
||||
_device->write(e->block, e->buffer);
|
||||
e->dirty = false;
|
||||
}
|
||||
}
|
||||
_device->sync();
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::write(unsigned block, const void *bp)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
|
||||
if (e)
|
||||
{
|
||||
e->dirty = true;
|
||||
std::memcpy(e->buffer, bp, 512);
|
||||
return;
|
||||
}
|
||||
|
||||
// returns a new entry not in the hashtable or the linked list.
|
||||
// we add it to both.
|
||||
e = newEntry(block);
|
||||
|
||||
e->count = 0;
|
||||
e->dirty = true;
|
||||
|
||||
std::memcpy(e->buffer, bp, 512);
|
||||
|
||||
addEntry(e);
|
||||
setLast(e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ConcreteBlockCache::markDirty(unsigned block)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
|
||||
if (e) e->dirty = true;
|
||||
// error otherwise?
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::release(unsigned block, int flags)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
bool dirty = flags & (kBlockDirty | kBlockCommitNow);
|
||||
|
||||
if (e)
|
||||
{
|
||||
if (dirty) e->dirty = true;
|
||||
|
||||
decrementCount(e);
|
||||
|
||||
if (flags & kBlockCommitNow)
|
||||
{
|
||||
_device->write(block, e->buffer);
|
||||
e->dirty = false;
|
||||
}
|
||||
|
||||
}
|
||||
// error otherwise?
|
||||
}
|
||||
|
||||
void *ConcreteBlockCache::acquire(unsigned block)
|
||||
{
|
||||
Entry *e = findEntry(block);
|
||||
|
||||
if (e)
|
||||
{
|
||||
incrementCount(e);
|
||||
return e->buffer;
|
||||
}
|
||||
|
||||
// returns a new entry, not in hash table, not in free list.
|
||||
e = newEntry(block);
|
||||
|
||||
_device->read(block, e->buffer);
|
||||
|
||||
addEntry(e);
|
||||
|
||||
return e->buffer;
|
||||
}
|
||||
|
||||
unsigned ConcreteBlockCache::hashFunction(unsigned block)
|
||||
{
|
||||
return block % HashTableSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ConcreteBlockCache::Entry *ConcreteBlockCache::findEntry(unsigned block)
|
||||
{
|
||||
Entry *e;
|
||||
unsigned hash = hashFunction(block);
|
||||
|
||||
e = _hashTable[hash];
|
||||
|
||||
while ((e) && (e->block != block))
|
||||
e = e->nextHash;
|
||||
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* remove a block from the hashtable
|
||||
* and write to dick if dirty.
|
||||
*/
|
||||
void ConcreteBlockCache::removeEntry(unsigned block)
|
||||
{
|
||||
Entry *e;
|
||||
Entry *prev;
|
||||
unsigned hash = hashFunction(block);
|
||||
|
||||
e = _hashTable[hash];
|
||||
if (!e) return;
|
||||
|
||||
// head pointer, special case.
|
||||
if (e->block == block)
|
||||
{
|
||||
_hashTable[hash] = e->nextHash;
|
||||
e->nextHash = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
prev = e;
|
||||
e = e->nextHash;
|
||||
|
||||
if (!e) break;
|
||||
if (e->block == block)
|
||||
{
|
||||
prev->nextHash = e->nextHash;
|
||||
e->nextHash = NULL;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::addEntry(Entry *e)
|
||||
{
|
||||
unsigned hash = hashFunction(e->block);
|
||||
|
||||
e->nextHash = _hashTable[hash];
|
||||
_hashTable[hash] = e;
|
||||
}
|
||||
|
||||
// increment the count and remove from the free list
|
||||
// if necessary.
|
||||
void ConcreteBlockCache::incrementCount(Entry *e)
|
||||
{
|
||||
|
||||
if (e->count == 0)
|
||||
{
|
||||
Entry *prev = e->prev;
|
||||
Entry *next = e->next;
|
||||
|
||||
e->prev = e->next = NULL;
|
||||
|
||||
if (prev) prev->next = next;
|
||||
if (next) next->prev = prev;
|
||||
|
||||
if (_first == e) _first = next;
|
||||
if (_last == e) _last = prev;
|
||||
}
|
||||
|
||||
e->count = e->count + 1;
|
||||
|
||||
}
|
||||
|
||||
// decrement the count. If it goes to 0,
|
||||
// add it as the last block entry.
|
||||
void ConcreteBlockCache::decrementCount(Entry *e)
|
||||
{
|
||||
e->count = e->count - 1;
|
||||
if (e->count == 0)
|
||||
{
|
||||
setLast(e);
|
||||
}
|
||||
}
|
||||
|
||||
ConcreteBlockCache::Entry *ConcreteBlockCache::newEntry(unsigned block)
|
||||
{
|
||||
Entry *e;
|
||||
|
||||
if (_first)
|
||||
{
|
||||
e = _first;
|
||||
if (_first == _last)
|
||||
{
|
||||
_first = _last = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
_first = e->next;
|
||||
_first->prev = NULL;
|
||||
}
|
||||
|
||||
|
||||
if (e->dirty)
|
||||
{
|
||||
_device->write(e->block, e->buffer);
|
||||
e->dirty = false;
|
||||
}
|
||||
removeEntry(e->block);
|
||||
}
|
||||
else
|
||||
{
|
||||
e = new Entry;
|
||||
_buffers.push_back(e);
|
||||
}
|
||||
|
||||
e->next = NULL;
|
||||
e->prev= NULL;
|
||||
e->nextHash = NULL;
|
||||
e->count = 1;
|
||||
e->block = block;
|
||||
e->dirty = false;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::setLast(Entry *e)
|
||||
{
|
||||
if (_last == NULL)
|
||||
{
|
||||
_first = _last = e;
|
||||
return;
|
||||
}
|
||||
|
||||
e->prev = _last;
|
||||
_last->next = e;
|
||||
_last = e;
|
||||
}
|
||||
|
||||
|
||||
void ConcreteBlockCache::setFirst(Entry *e)
|
||||
{
|
||||
if (_first == NULL)
|
||||
{
|
||||
_first = _last = e;
|
||||
return;
|
||||
}
|
||||
|
||||
e->next = _first;
|
||||
_first->prev = e;
|
||||
_first = e;
|
||||
}
|
||||
|
70
Cache/ConcreteBlockCache.h
Normal file
70
Cache/ConcreteBlockCache.h
Normal file
@ -0,0 +1,70 @@
|
||||
#ifndef __CONCRETE_BLOCK_CACHE_H__
|
||||
#define __CONCRETE_BLOCK_CACHE_H__
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Cache/BlockCache.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class ConcreteBlockCache : public BlockCache {
|
||||
public:
|
||||
ConcreteBlockCache(BlockDevice *device, unsigned size = 16);
|
||||
virtual ~ConcreteBlockCache();
|
||||
|
||||
virtual void sync();
|
||||
virtual void write(unsigned block, const void *vp);
|
||||
|
||||
|
||||
virtual void *acquire(unsigned block);
|
||||
virtual void release(unsigned block, int flags);
|
||||
virtual void markDirty(unsigned block);
|
||||
|
||||
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
unsigned block;
|
||||
unsigned count;
|
||||
bool dirty;
|
||||
|
||||
struct Entry *next;
|
||||
struct Entry *prev;
|
||||
struct Entry *nextHash;
|
||||
|
||||
uint8_t buffer[512];
|
||||
|
||||
};
|
||||
|
||||
typedef std::vector<Entry *>::iterator EntryIter;
|
||||
|
||||
enum { HashTableSize = 23 };
|
||||
|
||||
std::vector<Entry *>_buffers;
|
||||
|
||||
Entry *_hashTable[HashTableSize];
|
||||
|
||||
Entry *_first;
|
||||
Entry *_last;
|
||||
|
||||
|
||||
unsigned hashFunction(unsigned block);
|
||||
|
||||
Entry *findEntry(unsigned block);
|
||||
void removeEntry(unsigned block);
|
||||
void addEntry(Entry *);
|
||||
|
||||
Entry *newEntry(unsigned block);
|
||||
|
||||
void pushEntry(Entry *);
|
||||
|
||||
void setLast(Entry *);
|
||||
void setFirst(Entry *);
|
||||
|
||||
void incrementCount(Entry *);
|
||||
void decrementCount(Entry *);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
134
Cache/MappedBlockCache.cpp
Normal file
134
Cache/MappedBlockCache.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <Cache/MappedBlockCache.h>
|
||||
#include <Device/BlockDevice.h>
|
||||
|
||||
#include <ProFUSE/Exception.h>
|
||||
|
||||
|
||||
|
||||
using namespace Device;
|
||||
|
||||
using ProFUSE::Exception;
|
||||
using ProFUSE::POSIXException;
|
||||
|
||||
|
||||
MappedBlockCache::MappedBlockCache(BlockDevice *device, void *data) :
|
||||
BlockCache(device)
|
||||
{
|
||||
_data = (uint8_t *)data;
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
MappedBlockCache::~MappedBlockCache()
|
||||
{
|
||||
if (_dirty) sync();
|
||||
}
|
||||
|
||||
|
||||
void *MappedBlockCache::acquire(unsigned block)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::load"
|
||||
|
||||
if (block >= blocks())
|
||||
throw Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
|
||||
return _data + block * 512;
|
||||
}
|
||||
|
||||
void MappedBlockCache::release(unsigned block, int flags)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::unload"
|
||||
|
||||
// kBlockCommitNow implies kBlockDirty.
|
||||
if (flags & kBlockCommitNow)
|
||||
{
|
||||
sync(block);
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & kBlockDirty) _dirty = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MappedBlockCache::write(unsigned block, const void *vp)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::write"
|
||||
|
||||
if (block >= blocks())
|
||||
throw Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
_dirty = true;
|
||||
std::memcpy(_data + block * 512, vp, 512);
|
||||
}
|
||||
|
||||
|
||||
void MappedBlockCache::zeroBlock(unsigned block)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::zeroBlock"
|
||||
|
||||
if (block >= blocks())
|
||||
throw Exception(__METHOD__ ": Invalid block.");
|
||||
|
||||
|
||||
_dirty = true;
|
||||
std::memset(_data + block * 512, 0, 512);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// sync everything.
|
||||
void MappedBlockCache::sync()
|
||||
{
|
||||
_device->sync();
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* sync an individual page.
|
||||
*
|
||||
*/
|
||||
void MappedBlockCache::sync(unsigned block)
|
||||
{
|
||||
#undef __METHOD__
|
||||
#define __METHOD__ "MappedBlockCache::sync"
|
||||
|
||||
|
||||
int pagesize = ::getpagesize();
|
||||
|
||||
void *start = _data + block * 512;
|
||||
void *end = _data + 512 + block * 512;
|
||||
|
||||
|
||||
start = (void *)((ptrdiff_t)start / pagesize * pagesize);
|
||||
end = (void *)((ptrdiff_t)end / pagesize * pagesize);
|
||||
|
||||
if (::msync(start, pagesize, MS_SYNC) != 0)
|
||||
throw POSIXException(__METHOD__ ": msync", errno);
|
||||
|
||||
if (start != end)
|
||||
{
|
||||
if (::msync(end, pagesize, MS_SYNC) != 0)
|
||||
throw POSIXException(__METHOD__ ": msync", errno);
|
||||
}
|
||||
}
|
||||
|
||||
void MappedBlockCache::markDirty(unsigned block)
|
||||
{
|
||||
_dirty = true;
|
||||
}
|
||||
|
35
Cache/MappedBlockCache.h
Normal file
35
Cache/MappedBlockCache.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __MAPPED_BLOCK_CACHE_H__
|
||||
#define __MAPPED_BLOCK_CACHE_H__
|
||||
|
||||
#include <Cache/BlockCache.h>
|
||||
|
||||
namespace Device {
|
||||
|
||||
class MappedBlockCache : public BlockCache {
|
||||
public:
|
||||
|
||||
MappedBlockCache(BlockDevice *, void *data);
|
||||
virtual ~MappedBlockCache();
|
||||
|
||||
virtual void sync();
|
||||
virtual void write(unsigned block, const void *vp);
|
||||
|
||||
virtual void zeroBlock(unsigned block);
|
||||
|
||||
|
||||
virtual void *acquire(unsigned block);
|
||||
virtual void release(unsigned block, int flags);
|
||||
virtual void markDirty(unsigned block);
|
||||
|
||||
private:
|
||||
|
||||
void sync(unsigned block);
|
||||
|
||||
uint8_t *_data;
|
||||
bool _dirty;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user