2009-12-08 00:01:24 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
2009-12-11 00:59:53 +00:00
|
|
|
#include <cerrno>
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/mman.h>
|
2009-12-12 19:27:08 +00:00
|
|
|
#include <unistd.h>
|
2009-12-11 00:59:53 +00:00
|
|
|
|
2009-12-16 01:33:28 +00:00
|
|
|
#include <Device/BlockDevice.h>
|
|
|
|
#include <Device/BlockCache.h>
|
|
|
|
|
|
|
|
#include <ProFUSE/Exception.h>
|
|
|
|
#include <ProFUSE/auto.h>
|
2009-12-08 00:01:24 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note -- everything is assumed to be single-threaded.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-12-16 01:33:28 +00:00
|
|
|
using namespace Device;
|
|
|
|
|
|
|
|
using ProFUSE::Exception;
|
|
|
|
using ProFUSE::POSIXException;
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
#pragma mark -
|
2010-03-13 18:51:00 +00:00
|
|
|
#pragma mark BlockCache
|
|
|
|
|
|
|
|
BlockCache::~BlockCache(BlockDevice *device)
|
|
|
|
{
|
|
|
|
_device = _device;
|
|
|
|
_blocks = device->blocks();
|
|
|
|
_readOnly = device->readOnly();
|
|
|
|
}
|
2009-12-11 00:59:53 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
BlockCache::~BlockCache()
|
2009-12-11 00:59:53 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
delete _device;
|
2009-12-11 00:59:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark MappedBlockCache
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
MappedBlockCache::MappedBlockCache(void *data, unsigned blocks)
|
|
|
|
{
|
|
|
|
_blocks = blocks;
|
2009-12-12 19:27:08 +00:00
|
|
|
_data = (uint8_t *)data;
|
2009-12-11 00:59:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MappedBlockCache::write()
|
|
|
|
{
|
|
|
|
// TODO...
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2009-12-11 00:59:53 +00:00
|
|
|
void *MappedBlockCache::load(unsigned block)
|
|
|
|
{
|
|
|
|
#undef __METHOD__
|
|
|
|
#define __METHOD__ "MappedBlockCache::load"
|
|
|
|
|
|
|
|
if (block >= _blocks)
|
|
|
|
throw Exception(__METHOD__ ": Invalid block.");
|
|
|
|
|
|
|
|
|
|
|
|
return _data + block * 512;
|
|
|
|
}
|
|
|
|
void MappedBlockCache::unload(unsigned block, bool dirty)
|
|
|
|
{
|
|
|
|
#undef __METHOD__
|
|
|
|
#define __METHOD__ "MappedBlockCache::unload"
|
|
|
|
|
|
|
|
if (!dirty) return;
|
2009-12-12 19:27:08 +00:00
|
|
|
|
|
|
|
// msync must be page-size aligned.
|
|
|
|
unsigned pagesize = ::getpagesize();
|
|
|
|
unsigned offset = block * 512;
|
|
|
|
void *address = _data + offset / pagesize * pagesize;
|
|
|
|
unsigned length = offset % pagesize + 512;
|
|
|
|
|
|
|
|
if (::msync(address, length, MS_ASYNC) < 0)
|
2009-12-11 00:59:53 +00:00
|
|
|
{
|
|
|
|
throw POSIXException(__METHOD__ ": msync failed.", errno);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
2010-03-13 18:51:00 +00:00
|
|
|
#pragma mark ConcreteBlockCache
|
2009-12-11 00:59:53 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
typedef std::vector<BlockCache::Entry *>::iterator EntryIter;
|
2009-12-11 00:59:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
ConcreteBlockCache::ConcreteBlockCache(BlockDevice *device, unsigned size) :
|
|
|
|
BlockCache(device)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
ConcreteBlockCache::~ConcreteBlockCache()
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
EntryIter iter;
|
|
|
|
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
Entry *e = *iter;
|
|
|
|
|
|
|
|
if (e->dirty)
|
|
|
|
{
|
|
|
|
_device->write(e->block, e->buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete e;
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
2010-03-13 18:51:00 +00:00
|
|
|
_device->sync();
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
|
|
|
|
ConcreteBlockCache::sync()
|
2009-12-11 00:59:53 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
EntryIter iter;
|
|
|
|
for (iter = _buffers.begin(); iter != _buffers.end(); ++iter)
|
2009-12-11 00:59:53 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
Entry *e = *iter;
|
|
|
|
|
|
|
|
if (e->dirty)
|
2009-12-11 00:59:53 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
_device->write(e->block, e->buffer);
|
|
|
|
e->dirty = false;
|
2009-12-11 00:59:53 +00:00
|
|
|
}
|
|
|
|
}
|
2010-03-13 18:51:00 +00:00
|
|
|
_device->sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ConcreteBlockCache::markDirty(unsigned block)
|
|
|
|
{
|
|
|
|
Entry *e = findEntry(block);
|
|
|
|
|
|
|
|
if (e) e->dirty = true;
|
|
|
|
// error otherwise?
|
2009-12-11 00:59:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
void ConcreteBlockCache::release(unsigned block, bool dirty)
|
|
|
|
{
|
|
|
|
Entry *e = findEntry(block);
|
|
|
|
|
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
if (dirty) e->dirty = true;
|
|
|
|
|
|
|
|
decrementCount(e);
|
|
|
|
}
|
|
|
|
// error otherwise?
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
void *ConcreteBlockCache::acquire(unsigned block)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
Entry *e = findEntry(block);
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
if (e)
|
|
|
|
{
|
|
|
|
incrementCount(e);
|
|
|
|
return e->buffer;
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
unsigned hash = hashFunction(block);
|
|
|
|
|
|
|
|
// returns a new entry, not in hash table, not in free list.
|
|
|
|
e = newEntry(block);
|
|
|
|
|
|
|
|
_device->read(block, e->buffer);
|
|
|
|
|
|
|
|
e->nextHash = _hashTable[hash];
|
|
|
|
_hashTable[hash] = e;
|
|
|
|
|
|
|
|
return e->buffer;
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
unsigned ConcreteBlockCache::hashFunction(unsigned block)
|
|
|
|
{
|
|
|
|
return block % HashTableSize;
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
/*
|
|
|
|
* remove a block from the hashtable
|
|
|
|
* and write to dick if dirty.
|
|
|
|
*/
|
|
|
|
void removeEntry(unsigned block)
|
|
|
|
{
|
|
|
|
Entry *e;
|
|
|
|
Entry *prev;
|
|
|
|
unsigned hash = hashFunction(block);
|
|
|
|
|
|
|
|
e = _hashTable[hash];
|
|
|
|
if (!e) return;
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
// head pointer, special case.
|
|
|
|
if (e->block == block)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
_hashTable[hash] = e->nextHash;
|
|
|
|
e->nextHash = NULL;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
prev = e;
|
|
|
|
e = e->next;
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
if (!e) break;
|
|
|
|
if (e->block == block)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
prev->nextHash = e->nextHash;
|
|
|
|
e->nextHash = NULL;
|
|
|
|
|
|
|
|
if (e->dirty)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
_device->write(e->block, e->buffer);
|
|
|
|
e->dirty = false;
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
2010-03-13 18:51:00 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
2010-03-13 18:51:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// increment the count and remove from the free list
|
|
|
|
// if necessary.
|
|
|
|
void ConcreteBlockCache::incrementCount(Entry *e)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (e->count == 0)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
Entry *prev = e->prev;
|
|
|
|
Entry *next = e->next;
|
2009-12-11 00:59:53 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
e->prev = e->next = NULL;
|
|
|
|
|
|
|
|
if (prev) prev->next = next;
|
|
|
|
if (next) next->prev = prev;
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
if (_first == e) _first = next;
|
|
|
|
if (_last == e) _last = prev;
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
e->count = e->count + 1;
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
if (_last == NULL)
|
|
|
|
{
|
|
|
|
_first = _last = e;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e->prev = _last;
|
|
|
|
_last = e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Entry *ConcreteBlockCache::newEntry(unsigned block)
|
|
|
|
{
|
|
|
|
Entry *e;
|
|
|
|
|
|
|
|
if (_first)
|
|
|
|
{
|
|
|
|
e = _first;
|
|
|
|
if (_first == _last)
|
|
|
|
{
|
|
|
|
_first = _last = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_first = e->next;
|
|
|
|
_first->prev = NULL;
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
removeEntry(e->block);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
e = new Entry;
|
|
|
|
_buffers.push_back(e);
|
|
|
|
}
|
2009-12-08 00:01:24 +00:00
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
e->next = NULL;
|
|
|
|
e->prev= NULL;
|
|
|
|
e->nextHash = NULL;
|
|
|
|
e->count = 1;
|
|
|
|
e->block = block;
|
|
|
|
e->dirty = false;
|
|
|
|
|
|
|
|
return e;
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-13 18:51:00 +00:00
|
|
|
void ConcreteBlockCache::setLast(Entry *e)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
if (_last == NULL)
|
2009-12-08 00:01:24 +00:00
|
|
|
{
|
2010-03-13 18:51:00 +00:00
|
|
|
_first = _last = e;
|
|
|
|
return;
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|
2010-03-13 18:51:00 +00:00
|
|
|
|
|
|
|
e->prev = _last;
|
|
|
|
_last->next = e;
|
|
|
|
_last = e;
|
2009-12-08 00:01:24 +00:00
|
|
|
}
|