From 14e9b43f32efc3ed8d9b248e8193d9ce5fe3a744 Mon Sep 17 00:00:00 2001 From: ksherlock Date: Sat, 19 Feb 2011 18:05:57 +0000 Subject: [PATCH] new branch to integrate BlockDevice, BlockCache git-svn-id: https://profuse.googlecode.com/svn/branches/profuse_interim@331 aa027e90-d47c-11dd-86d7-074df07e0730 --- Cache/BlockCache.cpp | 73 +++++++ Cache/BlockCache.h | 61 ++++++ Cache/ConcreteBlockCache.cpp | 375 +++++++++++++++++++++++++++++++++++ Cache/ConcreteBlockCache.h | 70 +++++++ Cache/MappedBlockCache.cpp | 134 +++++++++++++ Cache/MappedBlockCache.h | 35 ++++ 6 files changed, 748 insertions(+) create mode 100644 Cache/BlockCache.cpp create mode 100644 Cache/BlockCache.h create mode 100644 Cache/ConcreteBlockCache.cpp create mode 100644 Cache/ConcreteBlockCache.h create mode 100644 Cache/MappedBlockCache.cpp create mode 100644 Cache/MappedBlockCache.h diff --git a/Cache/BlockCache.cpp b/Cache/BlockCache.cpp new file mode 100644 index 0000000..b6670b5 --- /dev/null +++ b/Cache/BlockCache.cpp @@ -0,0 +1,73 @@ + +#include +#include +#include + +#include +#include +#include + + +#include +#include + +#include +#include + + + + +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); +} diff --git a/Cache/BlockCache.h b/Cache/BlockCache.h new file mode 100644 index 0000000..c4587d1 --- /dev/null +++ b/Cache/BlockCache.h @@ -0,0 +1,61 @@ +#ifndef __BLOCKCACHE_H__ +#define __BLOCKCACHE_H__ + +#include +#include + +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 diff --git a/Cache/ConcreteBlockCache.cpp b/Cache/ConcreteBlockCache.cpp new file mode 100644 index 0000000..5e0bd69 --- /dev/null +++ b/Cache/ConcreteBlockCache.cpp @@ -0,0 +1,375 @@ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + + +/* + * 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::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; +} + diff --git a/Cache/ConcreteBlockCache.h b/Cache/ConcreteBlockCache.h new file mode 100644 index 0000000..2ca5698 --- /dev/null +++ b/Cache/ConcreteBlockCache.h @@ -0,0 +1,70 @@ +#ifndef __CONCRETE_BLOCK_CACHE_H__ +#define __CONCRETE_BLOCK_CACHE_H__ + +#include + +#include + +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::iterator EntryIter; + + enum { HashTableSize = 23 }; + + std::vector_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 diff --git a/Cache/MappedBlockCache.cpp b/Cache/MappedBlockCache.cpp new file mode 100644 index 0000000..1d1be58 --- /dev/null +++ b/Cache/MappedBlockCache.cpp @@ -0,0 +1,134 @@ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + + + +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; +} + diff --git a/Cache/MappedBlockCache.h b/Cache/MappedBlockCache.h new file mode 100644 index 0000000..a3402ff --- /dev/null +++ b/Cache/MappedBlockCache.h @@ -0,0 +1,35 @@ +#ifndef __MAPPED_BLOCK_CACHE_H__ +#define __MAPPED_BLOCK_CACHE_H__ + +#include + +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 +