#include #include #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 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 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 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; }