Files
haiku-beta6/src/kernel/vm2/area.C
T
Michael Phipps ceb3763e56 Implemented the sharing of vnodes. Hopefully mmap and file cache and cloned
areas will not step on each others feet...
Added comments all over the place.
Bugs squished


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2248 a95241bf-73f2-0310-859d-f6bbb57e9c96
2002-12-15 07:05:38 +00:00

296 lines
9.6 KiB
C++

#include <new.h>
#include "area.h"
#include "areaManager.h"
#include "vpage.h"
#include "vnodePool.h"
#include "vpagePool.h"
#include "vmHeaderBlock.h"
extern vmHeaderBlock *vmBlock;
ulong vpageHash (node &vp) {return reinterpret_cast <vpage &>(vp).hash();}
bool vpageisEqual (node &vp,node &vp2) {return reinterpret_cast <vpage &>(vp)==reinterpret_cast <vpage &>(vp2);}
// Simple constructor; real work is later
area::area(void) : vpages(AREA_HASH_TABLE_SIZE) {
vpages.setHash(vpageHash);
vpages.setIsEqual(vpageisEqual);
}
// Not much here, either
void area::setup (areaManager *myManager) {
//error ("area::setup setting up new area\n");
manager=myManager;
//error ("area::setup done setting up new area\n");
}
// Decide which algorithm to use for finding the next virtual address and try to find one.
unsigned long area::mapAddressSpecToAddress(addressSpec type,void * req,int pageCount) {
// We will lock in the callers
unsigned long base,requested=(unsigned long)req;
switch (type) {
case EXACT:
base=manager->getNextAddress(pageCount,requested);
if (base!=requested)
return 0;
break;
case BASE:
base=manager->getNextAddress(pageCount,requested);
break;
case ANY:
base=manager->getNextAddress(pageCount,USER_BASE);
break;
case ANY_KERNEL:
base=manager->getNextAddress(pageCount,KERNEL_BASE);
break;
case CLONE: base=0;break; // Not sure what to do...
default: // should never happen
throw ("Unknown type passed to mapAddressSpecToAddress");
}
// error ("area::mapAddressSpecToAddress, in type: %s, address = %x, size = %d\n", ((type==EXACT)?"Exact":(type==BASE)?"BASE":(type==ANY)?"ANY":(type==CLONE)?"CLONE":"ANY_KERNEL"), requested,pageCount);
return base;
}
// This is the really interesting part of creating an area
status_t area::createAreaGuts( char *inName, int pageCount, void **address, addressSpec type, pageState inState, protectType protect, bool inFinalWrite, int fd, size_t offset, area *originalArea=NULL /* For clone only*/) {
error ("area::createAreaGuts : name = %s, pageCount = %d, address = %lx, addressSpec = %d, pageState = %d, protection = %d, inFinalWrite = %d, fd = %d, offset = %d,originalArea=%ld\n",
inName,pageCount,address,type,inState,protect,inFinalWrite,fd,offset,originalArea);
vpage *newPage;
// Get an address to start this area at
unsigned long base=mapAddressSpecToAddress(type,*address,pageCount);
if (base==0)
return B_ERROR;
// Set up some basic info
strcpy(name,inName);
state=inState;
start_address=base;
end_address=base+(pageCount*PAGE_SIZE)-1;
*address=(void *)base;
finalWrite=inFinalWrite;
// For non-cloned areas, make a new vpage for every page necesssary.
if (originalArea==NULL) // Not for cloning
for (int i=0;i<pageCount;i++) {
newPage=new (vmBlock->vpagePool->get()) vpage;
if (fd) {
vnode newVnode;
newVnode.fd=fd;
newVnode.offset=offset;
// vmBlock->vnodeManager->addVNode(newVnode,newPage);
newPage->setup(base+PAGE_SIZE*i,&newVnode,NULL,protect,inState);
}
else
newPage->setup(base+PAGE_SIZE*i,NULL,NULL,protect,inState);
vpages.add(newPage);
}
else // cloned
// Need to lock other area, here, just in case...
// Make a copy of each page in the other area...
for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur;
newPage=new (vmBlock->vpagePool->get()) vpage;
newPage->setup(base,page->getBacking(),page->getPhysPage(),protect,inState);// Cloned area has the same physical page and backing store...
vpages.add(newPage);
base+=PAGE_SIZE;
}
dump();
return B_OK;
}
status_t area::createAreaMappingFile(char *inName, int pageCount,void **address, addressSpec type,pageState inState,protectType protect,int fd,size_t offset) {
return createAreaGuts(inName,pageCount,address,type,inState,protect,true,fd,offset);
}
status_t area::createArea(char *inName, int pageCount,void **address, addressSpec type,pageState inState,protectType protect) {
return createAreaGuts(inName,pageCount,address,type,inState,protect,false,0,0);
}
// Clone another area.
status_t area::cloneArea(area *origArea, char *inName, void **address, addressSpec type,pageState inState,protectType protect) {
if (type==CLONE) {
*address=(void *)(origArea->getStartAddress());
type=EXACT;
}
if (origArea->getAreaManager()!=manager) { // If they are in different areas...
origArea->getAreaManager()->lock(); // This is just begging for a deadlock...
status_t retVal = createAreaGuts(inName,origArea->getPageCount(),address,type,inState,protect,false,0,0,origArea);
origArea->getAreaManager()->unlock();
return retVal;
}
else
return createAreaGuts(inName,origArea->getPageCount(),address,type,inState,protect,false,0,0,origArea);
}
// To free an area, interate over its poges, final writing them if necessary, then call cleanup and put the vpage back in the pool
void area::freeArea(void) {
//error ("area::freeArea: starting \n");
// vpages.dump();
node *cur;
for (hashIterate hi(vpages);node *cur=hi.get();) {
//error ("area::freeArea: wasting a page: %x\n",cur);
vpage *page=reinterpret_cast<vpage *>(cur);
if (finalWrite)
page->flush();
//error ("area::freeArea: flushed a page \n");
page->cleanup();
//page->next=NULL;
vmBlock->vpagePool->put(page);
}
vpages.~hashTable();
//error ("area::freeArea: unlocking \n");
//error ("area::freeArea: ending \n");
}
// Get area info
status_t area::getInfo(area_info *dest) {
dest->area=areaID;
strcpy(dest->name,name);
dest->size=end_address-start_address;
dest->lock=state;
dest->protection=protection;
dest->team=manager->getTeam();
dest->ram_size=0;
dest->in_count=0;
dest->out_count=0;
dest->copy_count=0;
for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur;
if (page->isMapped())
dest->ram_size+=PAGE_SIZE;
dest->in_count+=PAGE_SIZE;
dest->out_count+=PAGE_SIZE;
dest->copy_count+=PAGE_SIZE;
}
dest->address=(void *)start_address;
return B_OK;
}
bool area::contains(void *address) {
unsigned long base=(unsigned long)(address);
// error ("area::contains: looking for %d in %d -- %d, value = %d\n",base,start_address,end_address, ((start_address<=base) && (end_address>=base)));
return ((start_address<=base) && (base<=end_address));
}
// Resize an area.
status_t area::resize(size_t newSize) {
size_t oldSize =end_address-start_address;
// Duh. Nothing to do.
if (newSize==oldSize)
return B_OK;
// Grow the area. Figure out how many pages, allocate them and set them up
if (newSize>oldSize) {
int pageCount = (newSize - oldSize + PAGE_SIZE - 1) / PAGE_SIZE;
vpage *newPage;
for (int i=0;i<pageCount;i++) {
newPage=new (vmBlock->vpagePool->get()) vpage;
newPage->setup(end_address+PAGE_SIZE*i-1,NULL,NULL,protection,state);
vpages.add(newPage);
}
end_address+=start_address+newSize;
}
else { // Ewww. Shrinking. This is ugly right now.
int pageCount = (oldSize - newSize + PAGE_SIZE - 1) / PAGE_SIZE;
vpage *oldPage;
struct node *cur;
for (int i=0;i<pageCount;i++) { // This is probably really slow. OTOH, how often do people shrink their allocations?
node *max;
void *maxAddress=NULL,*curAddress;
for (hashIterate hi(vpages);node *cur=hi.get();)
if ((curAddress=(reinterpret_cast<vpage *>(cur))->getStartAddress()) > maxAddress) {
maxAddress=curAddress;
max=cur;
}
// Found the right one to removei; waste it, pool it, and move on
oldPage=reinterpret_cast<vpage *>(max);
vpages.remove(cur);
if (finalWrite)
oldPage->flush();
oldPage->cleanup();
vmBlock->vpagePool->put(oldPage);
}
}
return B_OK;
}
// When the protection for the area changes, the protection for every one of the pages must change
status_t area::setProtection(protectType prot) {
for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur;
page->setProtection(prot);
}
protection=prot;
return B_OK;
}
vpage *area::findVPage(unsigned long address) {
vpage findMe(address);
return reinterpret_cast <vpage *>(vpages.find(&findMe));
}
// To fault, find the vpage associated with the fault and call it's fault function
bool area::fault(void *fault_address, bool writeError) { // true = OK, false = panic.
vpage *page=findVPage((unsigned long)fault_address);
if (page)
return page->fault(fault_address,writeError);
else
return false;
}
char area::getByte(unsigned long address) { // This is for testing only
vpage *page=findVPage(address);
if (page)
return page->getByte(address,manager);
else
return 0;
}
void area::setByte(unsigned long address,char value) { // This is for testing only
vpage *page=findVPage(address);
if (page)
page->setByte(address,value,manager);
}
int area::getInt(unsigned long address) { // This is for testing only
vpage *page=findVPage(address);
if (page)
return page->getInt(address,manager);
else
return 0;
}
void area::setInt(unsigned long address,int value) { // This is for testing only
vpage *page=findVPage(address);
if (page)
page->setInt(address,value,manager);
}
// For every one of our vpages, call the vpage's pager
void area::pager(int desperation) {
for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur;
page->pager(desperation);
}
}
// For every one of our vpages, call the vpage's saver
void area::saver(void) {
for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur;
page->saver();
}
}
void area::dump(void) {
error ("area::dump: size = %ld, lock = %d, address = %lx\n",end_address-start_address,state,start_address);
vpages.dump();
for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur;
page->dump();
cur=cur->next;
}
}