167 lines
3.6 KiB
C++
167 lines
3.6 KiB
C++
#include <cassert>
|
|
#include <cstdio>
|
|
|
|
#include "mm.hh"
|
|
#include "phys_mm.hh"
|
|
#include "pagecache.hh"
|
|
|
|
PageCache::PageCache(int maxPages) : maxPages_(maxPages), curPages_(0) {
|
|
}
|
|
|
|
PageCache::~PageCache() {
|
|
for(auto& devEntry : allocs_) { // For each known device
|
|
for(auto& allocEntry : devEntry.second) {
|
|
freeAlloc(allocEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
PageCache::Handle PageCache::readAllocate(IBlockDevice& dev, unsigned pos, unsigned count) {
|
|
ScopedLock<Mutex> lock(mutex_);
|
|
|
|
_allocInfo *alloc = searchAlloc(&dev, pos, count);
|
|
if(!alloc) {
|
|
_allocInfo newAlloc;
|
|
buildAlloc(newAlloc, count);
|
|
|
|
newAlloc.dev = &dev;
|
|
newAlloc.start = pos;
|
|
|
|
try {
|
|
dev.read(newAlloc.start, newAlloc.virtMem, newAlloc.len);
|
|
allocs_[&dev].push_back(newAlloc);
|
|
alloc = &allocs_[&dev].back();
|
|
} catch(...) {
|
|
freeAlloc(newAlloc);
|
|
throw;
|
|
}
|
|
curPages_ += count;
|
|
}
|
|
|
|
return Handle(alloc, (pos - alloc->start));
|
|
}
|
|
|
|
PageCache::Handle PageCache::writeAllocate(IBlockDevice& dev, unsigned pos, unsigned count) {
|
|
ScopedLock<Mutex> lock(mutex_);
|
|
|
|
_allocInfo *alloc = searchAlloc(&dev, pos, count);
|
|
if(!alloc) {
|
|
_allocInfo newAlloc;
|
|
buildAlloc(newAlloc, count);
|
|
|
|
newAlloc.dev = &dev;
|
|
newAlloc.start = pos;
|
|
|
|
try {
|
|
allocs_[&dev].push_back(newAlloc);
|
|
alloc = &allocs_[&dev].back();
|
|
} catch(...) {
|
|
freeAlloc(newAlloc);
|
|
throw;
|
|
}
|
|
curPages_ += count;
|
|
}
|
|
|
|
return Handle(alloc, (pos - alloc->start));
|
|
}
|
|
|
|
void PageCache::eject(int lengthHint) {
|
|
ScopedLock<Mutex> lock(mutex_);
|
|
_eject(lengthHint);
|
|
}
|
|
|
|
|
|
void PageCache::freeAlloc(_allocInfo& alloc) {
|
|
assert(!alloc.refs);
|
|
if(alloc.dirty && alloc.dev)
|
|
alloc.dev->write(alloc.start, alloc.virtMem, alloc.len);
|
|
|
|
if(alloc.virtMem) {
|
|
cortexa8::unmap_pages((uintptr_t)alloc.virtMem, alloc.len);
|
|
mm::virtfree_pc((uintptr_t)alloc.virtMem);
|
|
}
|
|
if(alloc.physMem)
|
|
phys_mm::free(alloc.physMem);
|
|
}
|
|
|
|
void PageCache::buildAlloc(_allocInfo& alloc, unsigned len) {
|
|
if((maxPages_ > 0) && (curPages_ + len > (unsigned)maxPages_)) {
|
|
_eject(curPages_+len-maxPages_);
|
|
if(curPages_ + len > (unsigned)maxPages_)
|
|
throw ex::bad_alloc{};
|
|
}
|
|
|
|
alloc.dirty = false;
|
|
alloc.len = len;
|
|
alloc.dev = nullptr;
|
|
alloc.physMem = 0;
|
|
alloc.virtMem = nullptr;
|
|
alloc.refs = 0;
|
|
|
|
bool retry = true;
|
|
while(true) {
|
|
try {
|
|
if(!alloc.physMem)
|
|
alloc.physMem = phys_mm::alloc(len);
|
|
if(!alloc.virtMem)
|
|
alloc.virtMem = (char*)mm::virtalloc_pc(len);
|
|
cortexa8::map_pages((uintptr_t)alloc.virtMem, alloc.physMem, len);
|
|
break;
|
|
} catch(ex::bad_alloc &ex) {
|
|
if(retry) {
|
|
_eject(len);
|
|
retry = false;
|
|
} else {
|
|
freeAlloc(alloc);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PageCache::_allocInfo* PageCache::searchAlloc(IBlockDevice* dev, unsigned start, unsigned len) {
|
|
auto it = allocs_.find(dev);
|
|
if(it == allocs_.end())
|
|
return nullptr;
|
|
|
|
for(auto& allocEntry : it->second) {
|
|
if((allocEntry.start <= start) &&
|
|
((allocEntry.start + allocEntry.len) >= (start + len)))
|
|
return &allocEntry;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void PageCache::_eject(int lengthHint) {
|
|
printf("NYI: PageCache::_eject\n");
|
|
}
|
|
|
|
|
|
|
|
PageCache::Handle::Handle(_allocInfo* alloc, int ofs) : alloc_(alloc), ofs_(ofs) {
|
|
++alloc_->refs;
|
|
}
|
|
|
|
PageCache::Handle::Handle(Handle const& copy) : alloc_(copy.alloc_), ofs_(copy.ofs_) {
|
|
++alloc_->refs;
|
|
}
|
|
|
|
PageCache::Handle& PageCache::Handle::operator=(Handle const& copy) {
|
|
if(alloc_)
|
|
--alloc_->refs;
|
|
|
|
alloc_ = copy.alloc_;
|
|
ofs_ = copy.ofs_;
|
|
|
|
if(alloc_)
|
|
++alloc_->refs;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
PageCache::Handle::~Handle() {
|
|
--alloc_->refs;
|
|
}
|