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
This commit is contained in:
Michael Phipps 2002-12-15 07:05:38 +00:00
parent 2674719b33
commit ceb3763e56
15 changed files with 191 additions and 101 deletions

View File

@ -1,8 +1,8 @@
SubDir OBOS_TOP src kernel vm2 ; SubDir OBOS_TOP src kernel vm2 ;
BinCommand vmTest : error.C area.C areaManager.C cacheManager.C page.C pageManager.C swapFileManager.C vmInterface.C vpage.C areaPool.C vnodePool.C vpagePool.C test.C : root be ; BinCommand vmTest : error.C area.C areaManager.C cacheManager.C page.C pageManager.C swapFileManager.C vmInterface.C vpage.C areaPool.C vnodePool.C vpagePool.C vnodeManager.C test.C : root be ;
BinCommand simpleTest : error.C area.C areaManager.C cacheManager.C page.C pageManager.C swapFileManager.C vmInterface.C vpage.C areaPool.C vnodePool.C vpagePool.C simpleTest.C : root be ; BinCommand simpleTest : error.C area.C areaManager.C cacheManager.C page.C pageManager.C swapFileManager.C vmInterface.C vpage.C areaPool.C vnodePool.C vpagePool.C vnodeManager.C simpleTest.C : root be ;
BinCommand hashTest : error.C area.C areaManager.C cacheManager.C page.C pageManager.C swapFileManager.C vmInterface.C vpage.C areaPool.C vnodePool.C vpagePool.C hashTest.C : root be ; BinCommand hashTest : error.C area.C areaManager.C cacheManager.C page.C pageManager.C swapFileManager.C vmInterface.C vpage.C areaPool.C vnodePool.C vpagePool.C vnodeManager.C hashTest.C : root be ;
BinCommand olTest : error.C olTest.C : root be ; BinCommand olTest : error.C olTest.C : root be ;
BinCommand pmTest : error.C pageManager.C pageManTest.C page.C : root be ; BinCommand pmTest : error.C pageManager.C pageManTest.C page.C : root be ;

View File

@ -12,17 +12,20 @@ extern vmHeaderBlock *vmBlock;
ulong vpageHash (node &vp) {return reinterpret_cast <vpage &>(vp).hash();} 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);} 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) { area::area(void) : vpages(AREA_HASH_TABLE_SIZE) {
vpages.setHash(vpageHash); vpages.setHash(vpageHash);
vpages.setIsEqual(vpageisEqual); vpages.setIsEqual(vpageisEqual);
} }
// Not much here, either
void area::setup (areaManager *myManager) { void area::setup (areaManager *myManager) {
//error ("area::setup setting up new area\n"); //error ("area::setup setting up new area\n");
manager=myManager; manager=myManager;
//error ("area::setup done setting up new area\n"); //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) { unsigned long area::mapAddressSpecToAddress(addressSpec type,void * req,int pageCount) {
// We will lock in the callers // We will lock in the callers
unsigned long base,requested=(unsigned long)req; unsigned long base,requested=(unsigned long)req;
@ -49,39 +52,41 @@ unsigned long area::mapAddressSpecToAddress(addressSpec type,void * req,int page
return base; 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*/) { 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", 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); inName,pageCount,address,type,inState,protect,inFinalWrite,fd,offset,originalArea);
strcpy(name,inName);
vpage *newPage; vpage *newPage;
// Get an address to start this area at
unsigned long base=mapAddressSpecToAddress(type,*address,pageCount); unsigned long base=mapAddressSpecToAddress(type,*address,pageCount);
if (base==0) if (base==0)
return B_ERROR; return B_ERROR;
// Set up some basic info
strcpy(name,inName);
state=inState; state=inState;
start_address=base; start_address=base;
end_address=base+(pageCount*PAGE_SIZE)-1; end_address=base+(pageCount*PAGE_SIZE)-1;
*address=(void *)base; *address=(void *)base;
finalWrite=inFinalWrite; finalWrite=inFinalWrite;
// For non-cloned areas, make a new vpage for every page necesssary.
if (originalArea==NULL) // Not for cloning if (originalArea==NULL) // Not for cloning
for (int i=0;i<pageCount;i++) { for (int i=0;i<pageCount;i++) {
newPage=new (vmBlock->vpagePool->get()) vpage; newPage=new (vmBlock->vpagePool->get()) vpage;
if (fd) { if (fd) {
// vnode *newVnode=new (vmBlock->vnodePool->get()) vnode; vnode newVnode;
void *currentMemoryLocation; newVnode.fd=fd;
// A future implementation vnode *newVnode=vmBlock->vnodePool->getNode(fd,offset,PAGE_SIZE*i,true,currentMemoryLocation); newVnode.offset=offset;
vnode *newVnode=vmBlock->vnodePool->get(); // vmBlock->vnodeManager->addVNode(newVnode,newPage);
newVnode->fd=fd; newPage->setup(base+PAGE_SIZE*i,&newVnode,NULL,protect,inState);
newVnode->offset=offset;
newPage->setup(base+PAGE_SIZE*i,newVnode,NULL,protect,inState);
} }
else else
newPage->setup(base+PAGE_SIZE*i,NULL,NULL,protect,inState); newPage->setup(base+PAGE_SIZE*i,NULL,NULL,protect,inState);
vpages.add(newPage); vpages.add(newPage);
} }
else // cloned else // cloned
// Need to lock other area, here, just in case... // 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();) { for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur; vpage *page=(vpage *)cur;
newPage=new (vmBlock->vpagePool->get()) vpage; newPage=new (vmBlock->vpagePool->get()) vpage;
@ -101,12 +106,13 @@ status_t area::createArea(char *inName, int pageCount,void **address, addressSpe
return createAreaGuts(inName,pageCount,address,type,inState,protect,false,0,0); 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) { status_t area::cloneArea(area *origArea, char *inName, void **address, addressSpec type,pageState inState,protectType protect) {
if (type==CLONE) { if (type==CLONE) {
*address=(void *)(origArea->getStartAddress()); *address=(void *)(origArea->getStartAddress());
type=EXACT; type=EXACT;
} }
if (origArea->getAreaManager()!=manager) { if (origArea->getAreaManager()!=manager) { // If they are in different areas...
origArea->getAreaManager()->lock(); // This is just begging for a deadlock... 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); status_t retVal = createAreaGuts(inName,origArea->getPageCount(),address,type,inState,protect,false,0,0,origArea);
origArea->getAreaManager()->unlock(); origArea->getAreaManager()->unlock();
@ -116,6 +122,7 @@ status_t area::cloneArea(area *origArea, char *inName, void **address, addressSp
return createAreaGuts(inName,origArea->getPageCount(),address,type,inState,protect,false,0,0,origArea); 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) { void area::freeArea(void) {
//error ("area::freeArea: starting \n"); //error ("area::freeArea: starting \n");
@ -136,6 +143,7 @@ void area::freeArea(void) {
//error ("area::freeArea: ending \n"); //error ("area::freeArea: ending \n");
} }
// Get area info
status_t area::getInfo(area_info *dest) { status_t area::getInfo(area_info *dest) {
dest->area=areaID; dest->area=areaID;
strcpy(dest->name,name); strcpy(dest->name,name);
@ -166,10 +174,13 @@ bool area::contains(void *address) {
return ((start_address<=base) && (base<=end_address)); return ((start_address<=base) && (base<=end_address));
} }
// Resize an area.
status_t area::resize(size_t newSize) { status_t area::resize(size_t newSize) {
size_t oldSize =end_address-start_address; size_t oldSize =end_address-start_address;
// Duh. Nothing to do.
if (newSize==oldSize) if (newSize==oldSize)
return B_OK; return B_OK;
// Grow the area. Figure out how many pages, allocate them and set them up
if (newSize>oldSize) { if (newSize>oldSize) {
int pageCount = (newSize - oldSize + PAGE_SIZE - 1) / PAGE_SIZE; int pageCount = (newSize - oldSize + PAGE_SIZE - 1) / PAGE_SIZE;
vpage *newPage; vpage *newPage;
@ -180,7 +191,7 @@ status_t area::resize(size_t newSize) {
} }
end_address+=start_address+newSize; end_address+=start_address+newSize;
} }
else { else { // Ewww. Shrinking. This is ugly right now.
int pageCount = (oldSize - newSize + PAGE_SIZE - 1) / PAGE_SIZE; int pageCount = (oldSize - newSize + PAGE_SIZE - 1) / PAGE_SIZE;
vpage *oldPage; vpage *oldPage;
struct node *cur; struct node *cur;
@ -192,6 +203,7 @@ status_t area::resize(size_t newSize) {
maxAddress=curAddress; maxAddress=curAddress;
max=cur; max=cur;
} }
// Found the right one to removei; waste it, pool it, and move on
oldPage=reinterpret_cast<vpage *>(max); oldPage=reinterpret_cast<vpage *>(max);
vpages.remove(cur); vpages.remove(cur);
if (finalWrite) if (finalWrite)
@ -203,6 +215,7 @@ status_t area::resize(size_t newSize) {
return B_OK; 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) { status_t area::setProtection(protectType prot) {
for (hashIterate hi(vpages);node *cur=hi.get();) { for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur; vpage *page=(vpage *)cur;
@ -217,6 +230,7 @@ vpage *area::findVPage(unsigned long address) {
return reinterpret_cast <vpage *>(vpages.find(&findMe)); 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. bool area::fault(void *fault_address, bool writeError) { // true = OK, false = panic.
vpage *page=findVPage((unsigned long)fault_address); vpage *page=findVPage((unsigned long)fault_address);
if (page) if (page)
@ -253,6 +267,7 @@ void area::setInt(unsigned long address,int value) { // This is for testing only
page->setInt(address,value,manager); page->setInt(address,value,manager);
} }
// For every one of our vpages, call the vpage's pager
void area::pager(int desperation) { void area::pager(int desperation) {
for (hashIterate hi(vpages);node *cur=hi.get();) { for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur; vpage *page=(vpage *)cur;
@ -260,6 +275,7 @@ void area::pager(int desperation) {
} }
} }
// For every one of our vpages, call the vpage's saver
void area::saver(void) { void area::saver(void) {
for (hashIterate hi(vpages);node *cur=hi.get();) { for (hashIterate hi(vpages);node *cur=hi.get();) {
vpage *page=(vpage *)cur; vpage *page=(vpage *)cur;

View File

@ -10,6 +10,7 @@ bool areaIsLessThan(void *a,void *b)
return (((reinterpret_cast<area *>(a))->getStartAddress()) < (reinterpret_cast<area *>(b))->getStartAddress()); return (((reinterpret_cast<area *>(a))->getStartAddress()) < (reinterpret_cast<area *>(b))->getStartAddress());
} }
// This creates the one true lock for this area
areaManager::areaManager(void) areaManager::areaManager(void)
{ {
team=0; // should be proc_get_current_proc_id() team=0; // should be proc_get_current_proc_id()
@ -18,6 +19,7 @@ areaManager::areaManager(void)
areas.setIsLessThan(areaIsLessThan); areas.setIsLessThan(areaIsLessThan);
} }
// Loops over every area looking for someplace where we can get the space we need.
unsigned long areaManager::getNextAddress(int pages, unsigned long start) unsigned long areaManager::getNextAddress(int pages, unsigned long start)
{ {
// This function needs to deal with the possibility that we run out of address space... // This function needs to deal with the possibility that we run out of address space...
@ -39,6 +41,7 @@ unsigned long areaManager::getNextAddress(int pages, unsigned long start)
return start; return start;
} }
// Remove the area from our list, put it on the area pool and move on
void areaManager::freeArea(area_id areaID) void areaManager::freeArea(area_id areaID)
{ {
error ("areaManager::freeArea: begin\n"); error ("areaManager::freeArea: begin\n");
@ -68,6 +71,7 @@ area *areaManager::findAreaLock(void *address)
return retVal; return retVal;
} }
// Loops over our areas looking for this one by name
area *areaManager::findArea(char *address) area *areaManager::findArea(char *address)
{ {
error ("Finding area by string\n"); error ("Finding area by string\n");
@ -83,6 +87,7 @@ area *areaManager::findArea(char *address)
return retVal; return retVal;
} }
// Loops over our areas looking for the one whose virtual address matches the passed in address
area *areaManager::findArea(void *address) area *areaManager::findArea(void *address)
{ {
// THIS DOES NOT HAVE LOCKING - all callers must lock. // THIS DOES NOT HAVE LOCKING - all callers must lock.
@ -107,6 +112,7 @@ area *areaManager::findAreaLock(area_id id)
return retVal; return retVal;
} }
// Loops over our areas looking for the one whose ID was passed in
area *areaManager::findArea(area_id id) area *areaManager::findArea(area_id id)
{ {
//error ("Finding area by area_id\n"); //error ("Finding area by area_id\n");
@ -120,6 +126,7 @@ area *areaManager::findArea(area_id id)
return retVal; return retVal;
} }
// Find the area whose address matches this page fault and dispatch the fault to it.
bool areaManager::fault(void *fault_address, bool writeError) // true = OK, false = panic. bool areaManager::fault(void *fault_address, bool writeError) // true = OK, false = panic.
{ {
area *myArea; area *myArea;
@ -137,6 +144,7 @@ bool areaManager::fault(void *fault_address, bool writeError) // true = OK, fals
long areaManager::nextAreaID=0; long areaManager::nextAreaID=0;
// Create an area; get a new structure, call setup, create the guts, set its ID, add it to our list
int areaManager::createArea(char *AreaName,int pageCount,void **address, addressSpec addType,pageState state,protectType protect) int areaManager::createArea(char *AreaName,int pageCount,void **address, addressSpec addType,pageState state,protectType protect)
{ {
error ("areaManager::createArea - Creating an area\n"); error ("areaManager::createArea - Creating an area\n");
@ -159,6 +167,8 @@ int areaManager::createArea(char *AreaName,int pageCount,void **address, address
return retVal; return retVal;
} }
// FIX: THIS IS WRONG! It will only clone areas in our areaManager.
// Should: find the specified area, create a new area to be its clone, and set it up
int areaManager::cloneArea(int newAreaID,char *AreaName,void **address, addressSpec addType,pageState state,protectType protect) int areaManager::cloneArea(int newAreaID,char *AreaName,void **address, addressSpec addType,pageState state,protectType protect)
{ {
int retVal; int retVal;
@ -251,6 +261,7 @@ void areaManager::setInt(unsigned long address,int value)
unlock(); unlock();
} }
// Call pager for each of our areas
void areaManager::pager(int desperation) void areaManager::pager(int desperation)
{ {
lock(); lock();
@ -264,6 +275,7 @@ void areaManager::pager(int desperation)
unlock(); unlock();
} }
// Call saver for each of our areas
void areaManager::saver(void) void areaManager::saver(void)
{ {
lock(); lock();
@ -275,6 +287,7 @@ void areaManager::saver(void)
unlock(); unlock();
} }
// mmap is basically map POSIX values to ours and call createAreaMappingFile...
void *areaManager::mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) void *areaManager::mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
{ {
char name[MAXPATHLEN]; char name[MAXPATHLEN];
@ -306,6 +319,7 @@ void *areaManager::mmap(void *addr, size_t len, int prot, int flags, int fd, off
return addr; return addr;
} }
// Custom area destruction for mapped files
status_t areaManager::munmap(void *addr,size_t len) status_t areaManager::munmap(void *addr,size_t len)
{ {
// Note that this is broken for any and all munmaps that are not full area in size. This is an all or nothing game... // Note that this is broken for any and all munmaps that are not full area in size. This is an all or nothing game...

View File

@ -6,6 +6,7 @@
extern vmHeaderBlock *vmBlock; extern vmHeaderBlock *vmBlock;
// If we can get one from an existing block, cool. If not, get a new block, create as many as will fit in the block, put them on the free list and call ourself recursively
area *poolarea::get(void) area *poolarea::get(void)
{ {
area *ret=NULL; area *ret=NULL;

View File

@ -3,6 +3,7 @@
#include <vpagePool.h> #include <vpagePool.h>
#include "vmHeaderBlock.h" #include "vmHeaderBlock.h"
// functions for hash and isEqual. No surprises
ulong vnodeHash (node &vp) {vnode &vn=reinterpret_cast <vnode &>(vp); return vn.offset+vn.fd;} ulong vnodeHash (node &vp) {vnode &vn=reinterpret_cast <vnode &>(vp); return vn.offset+vn.fd;}
bool vnodeisEqual (node &vp,node &vp2) { bool vnodeisEqual (node &vp,node &vp2) {
vnode &vn=reinterpret_cast <vnode &>(vp); vnode &vn=reinterpret_cast <vnode &>(vp);
@ -13,19 +14,21 @@ bool vnodeisEqual (node &vp,node &vp2) {
extern vmHeaderBlock *vmBlock; extern vmHeaderBlock *vmBlock;
// TODO - we need to (somehow) make sure that the same vnodes here are shared with mmap. // TODO - we need to (somehow) make sure that the same vnodes here are shared with mmap.
// Maybe a vnode manager... // Maybe a vnode manager...
// Make the cache lockable
cacheManager::cacheManager(void) : area (),cacheMembers(30) { cacheManager::cacheManager(void) : area (),cacheMembers(30) {
myLock=create_sem(1,"Cache Manager Semaphore"); myLock=create_sem(1,"Cache Manager Semaphore");
cacheMembers.setHash(vnodeHash); cacheMembers.setHash(vnodeHash);
cacheMembers.setIsEqual(vnodeisEqual); cacheMembers.setIsEqual(vnodeisEqual);
} }
// Given a vnode and protection level, see if we have it in cache already
void *cacheManager::findBlock(vnode *target,bool readOnly) { void *cacheManager::findBlock(vnode *target,bool readOnly) {
cacheMember *candidate=reinterpret_cast<cacheMember *>(cacheMembers.find(target)); cacheMember *candidate=reinterpret_cast<cacheMember *>(cacheMembers.find(target));
if (!candidate || readOnly || candidate->vp->getProtection()>=writable) if (!candidate || readOnly || candidate->vp->getProtection()>=writable)
return candidate; return candidate;
// At this point, we have the first one in the hahs bucket. Loop over the hash bucket from now on, // At this point, we have the first one in the hash bucket. Loop over the hash bucket from now on,
// looking for an equality and writability match... // looking for an equality and writability match...
for (struct cacheMember *cur=candidate;cur;cur=reinterpret_cast<cacheMember *>(cur->next)) { for (struct cacheMember *cur=candidate;cur;cur=reinterpret_cast<cacheMember *>(cur->next)) {
if ((target==cur->vn) && (readOnly || (cur->vp->getProtection()>=writable))) if ((target==cur->vn) && (readOnly || (cur->vp->getProtection()>=writable)))
@ -36,6 +39,7 @@ void *cacheManager::findBlock(vnode *target,bool readOnly) {
return createBlock(target,false); return createBlock(target,false);
} }
// No cache hit found; have to make a new one. Find a virtual page, create a vnode, and map.
void *cacheManager::createBlock(vnode *target,bool readOnly, cacheMember *candidate) { void *cacheManager::createBlock(vnode *target,bool readOnly, cacheMember *candidate) {
bool foundSpot=false; bool foundSpot=false;
vpage *prev=NULL,*cur=NULL; vpage *prev=NULL,*cur=NULL;

View File

@ -4,6 +4,7 @@
static sem_id errorPrinting=0; static sem_id errorPrinting=0;
// Create a function for standardized formats. Wish I could get rid of the warning associated with this...
void error(char *fmt, ...) void error(char *fmt, ...)
{ {
if (errorPrinting==0) if (errorPrinting==0)

View File

@ -3,6 +3,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
// Handy function (actually handy for the casting) to add a long to a void *
void *addOffset(void *base,unsigned long offset) { void *addOffset(void *base,unsigned long offset) {
return (void *)(((unsigned long)base+offset)); return (void *)(((unsigned long)base+offset));
} }
@ -10,6 +11,7 @@ void *addOffset(void *base,unsigned long offset) {
pageManager::pageManager(void) { pageManager::pageManager(void) {
} }
// Since all of the physical pages will need page structures, allocate memory off of the top for them. Set up the lists and semaphores.
void pageManager::setup(void *area,int pages) { void pageManager::setup(void *area,int pages) {
// Calculate the number of pages that we will need to hold the page structures // Calculate the number of pages that we will need to hold the page structures
int pageOverhead=((pages*sizeof(page))+(PAGE_SIZE-1))/PAGE_SIZE; int pageOverhead=((pages*sizeof(page))+(PAGE_SIZE-1))/PAGE_SIZE;
@ -26,6 +28,7 @@ void pageManager::setup(void *area,int pages) {
error ("pageManager::setup - %d pages ready to rock and roll\n",unused.count()); error ("pageManager::setup - %d pages ready to rock and roll\n",unused.count());
} }
// Try to get a clean page first. If that fails, get a dirty one and clean it. Loop on this.
page *pageManager::getPage(void) { page *pageManager::getPage(void) {
page *ret=NULL; page *ret=NULL;
while (!ret) while (!ret)
@ -53,35 +56,7 @@ page *pageManager::getPage(void) {
return ret; return ret;
} }
bool pageManager::getContiguousPages(int pages,page **location) { // Take page from in use list and put it on the unused list
unsigned long current, start=0, next;
page *curPage;
int count=0;
while (count<pages) {
curPage=getPage();
current=curPage->getAddress();
if (start==0) {
start=current;
location[count++]=curPage;
}
else if (current==start+PAGE_SIZE*count) // This is the next one in line
location[count++]=curPage;
else if (current==start-PAGE_SIZE) { // Found the one directly previous
memmove(location[1],location[0],count*sizeof(page *));
start=current;
location[0]=curPage;
count++;
}
else { // Forget this series - it doesn't seem to be going anywhere...
while (--count>=0) {
freePage(location[count]);
location[count]=NULL;
}
}
}
return true;
}
void pageManager::freePage(page *toFree) { void pageManager::freePage(page *toFree) {
error ("pageManager::freePage; count = %d, address = %p\n",toFree->count,toFree); error ("pageManager::freePage; count = %d, address = %p\n",toFree->count,toFree);
if (atomic_add(&(toFree->count),-1)==1) { // atomic_add returns the *PREVIOUS* value. So we need to check to see if the one we are wasting was the last one. if (atomic_add(&(toFree->count),-1)==1) { // atomic_add returns the *PREVIOUS* value. So we need to check to see if the one we are wasting was the last one.
@ -97,6 +72,7 @@ void pageManager::freePage(page *toFree) {
} }
} }
// Loop forever cleaning any necessary pages
void pageManager::cleaner(void) { void pageManager::cleaner(void) {
while (1) { while (1) {
snooze(250000); snooze(250000);
@ -104,6 +80,7 @@ void pageManager::cleaner(void) {
} }
} }
// Find a page that needs cleaning. Take it from the "unused" list, clean it and put it on the clean list.
void pageManager::cleanOnePage(void) { void pageManager::cleanOnePage(void) {
if (unused.count()) { if (unused.count()) {
acquire_sem(unusedLock); acquire_sem(unusedLock);
@ -118,6 +95,7 @@ void pageManager::cleanOnePage(void) {
} }
} }
// Calculate how desperate we are for physical pages; 1 is not desperate at all, 5 is critical.
int pageManager::desperation(void) { // Formula to determine how desperate system is to get pages back... int pageManager::desperation(void) { // Formula to determine how desperate system is to get pages back...
int percentClean=(unused.count()+clean.count())*100/totalPages; int percentClean=(unused.count()+clean.count())*100/totalPages;
if (percentClean>30) return 1; if (percentClean>30) return 1;

View File

@ -13,7 +13,6 @@ class pageManager {
// Mutators // Mutators
page *getPage(void); page *getPage(void);
bool getContiguousPages(int pages,page **location);
// Accessors // Accessors
int desperation(void); int desperation(void);

View File

@ -8,6 +8,7 @@
extern vmHeaderBlock *vmBlock; extern vmHeaderBlock *vmBlock;
// Set up the swap file and make a semaphore to lock it
swapFileManager::swapFileManager(void) swapFileManager::swapFileManager(void)
{ {
swapFile = open("/boot/var/tmp/OBOS_swap",O_RDWR|O_CREAT,0x777 ); swapFile = open("/boot/var/tmp/OBOS_swap",O_RDWR|O_CREAT,0x777 );
@ -16,6 +17,7 @@ swapFileManager::swapFileManager(void)
lockFreeList=create_sem(1,"SwapFile Free List Semaphore"); // Should have team name in it. lockFreeList=create_sem(1,"SwapFile Free List Semaphore"); // Should have team name in it.
} }
// Try to get a page from the free list. If not, make a new page
vnode &swapFileManager::findNode(void) vnode &swapFileManager::findNode(void)
{ {
//error ("swapFileManager::findNode: Entering findNode \n"); //error ("swapFileManager::findNode: Entering findNode \n");
@ -35,15 +37,15 @@ vnode &swapFileManager::findNode(void)
//error (" New One: %d\n",newNode->offset); //error (" New One: %d\n",newNode->offset);
} }
newNode->valid=false; newNode->valid=false;
newNode->count=1;
//error ("swapFileManager::findNode: swapFileFreeList is now: "); //error ("swapFileManager::findNode: swapFileFreeList is now: ");
//swapFileFreeList.dump(); //swapFileFreeList.dump();
return *newNode; return *newNode;
} }
// Add this page to the free list.
void swapFileManager::freeVNode(vnode &v) void swapFileManager::freeVNode(vnode &v)
{ {
if ( atomic_add(&v.count,-1)==1) if (!v.vpages.count())
{ {
//error ("locking in sfm\n"); //error ("locking in sfm\n");
lock(); lock();

View File

@ -10,12 +10,11 @@ struct vnode : public node
int fd; int fd;
unsigned long offset; unsigned long offset;
bool valid; bool valid;
long count; list vpages;
vnode (void) vnode (void)
{ {
valid=false; valid=false;
count=0;
} }
}; };
#define B_OS_NAME_LENGTH 32 #define B_OS_NAME_LENGTH 32

View File

@ -9,6 +9,7 @@ class poolvnode;
class pageManager; class pageManager;
class swapFileManager; class swapFileManager;
class cacheManager; class cacheManager;
class vnodeManager;
#endif #endif
struct vmHeaderBlock struct vmHeaderBlock
@ -19,6 +20,7 @@ struct vmHeaderBlock
pageManager *pageMan; pageManager *pageMan;
swapFileManager *swapMan; swapFileManager *swapMan;
cacheManager *cacheMan; cacheManager *cacheMan;
vnodeManager *vnodeMan;
}; };
#endif #endif

View File

@ -7,6 +7,7 @@
#include "pageManager.h" #include "pageManager.h"
#include "swapFileManager.h" #include "swapFileManager.h"
#include "cacheManager.h" #include "cacheManager.h"
#include "vnodeManager.h"
#define I_AM_VM_INTERFACE #define I_AM_VM_INTERFACE
#include "vmHeaderBlock.h" #include "vmHeaderBlock.h"
#include "vmInterface.h" #include "vmInterface.h"
@ -63,6 +64,7 @@ int32 pagerThread(void *areaMan)
vmInterface::vmInterface(int pages) vmInterface::vmInterface(int pages)
{ {
// Make the area for testing
char temp[1000]; char temp[1000];
sprintf (temp,"vm_test_clone_%ld",getpid()); sprintf (temp,"vm_test_clone_%ld",getpid());
if (clone_area(temp,(void **)(&vmBlock),B_ANY_ADDRESS,B_WRITE_AREA,find_area("vm_test"))<0) if (clone_area(temp,(void **)(&vmBlock),B_ANY_ADDRESS,B_WRITE_AREA,find_area("vm_test"))<0)
@ -82,6 +84,7 @@ vmInterface::vmInterface(int pages)
exit(1); exit(1);
} }
error ("Need %d pages, creation calls for %d\n",pageCount,pages); error ("Need %d pages, creation calls for %d\n",pageCount,pages);
// Make all of the managers and the vmBlock to hold them
void *currentAddress = addToPointer(vmBlock,sizeof(struct vmHeaderBlock)); void *currentAddress = addToPointer(vmBlock,sizeof(struct vmHeaderBlock));
vmBlock->pageMan = new (currentAddress) pageManager; vmBlock->pageMan = new (currentAddress) pageManager;
currentAddress=addToPointer(currentAddress,sizeof(pageManager)); currentAddress=addToPointer(currentAddress,sizeof(pageManager));
@ -97,6 +100,8 @@ vmInterface::vmInterface(int pages)
currentAddress=addToPointer(currentAddress,sizeof(swapFileManager)); currentAddress=addToPointer(currentAddress,sizeof(swapFileManager));
vmBlock->cacheMan = new (currentAddress) cacheManager; vmBlock->cacheMan = new (currentAddress) cacheManager;
currentAddress=addToPointer(currentAddress,sizeof(cacheManager)); currentAddress=addToPointer(currentAddress,sizeof(cacheManager));
vmBlock->vnodeMan = new (currentAddress) vnodeManager;
currentAddress=addToPointer(currentAddress,sizeof(vnodeManager));
error ("Need %d pages, creation calls for %d\n",pageCount,pages); error ("Need %d pages, creation calls for %d\n",pageCount,pages);
error ("vmBlock is at %x, end of structures is at %x, pageMan called with address %x, pages = %d\n",vmBlock,currentAddress,addToPointer(vmBlock,PAGE_SIZE*pageCount),pages-pageCount); error ("vmBlock is at %x, end of structures is at %x, pageMan called with address %x, pages = %d\n",vmBlock,currentAddress,addToPointer(vmBlock,PAGE_SIZE*pageCount),pages-pageCount);
} }
@ -105,11 +110,13 @@ vmInterface::vmInterface(int pages)
error ("Area found!\n"); error ("Area found!\n");
} }
// Start the kernel daemons
resume_thread(tid_cleaner=spawn_thread(cleanerThread,"cleanerThread",0,(vmBlock->pageMan))); resume_thread(tid_cleaner=spawn_thread(cleanerThread,"cleanerThread",0,(vmBlock->pageMan)));
resume_thread(tid_saver=spawn_thread(saverThread,"saverThread",0,getAM())); resume_thread(tid_saver=spawn_thread(saverThread,"saverThread",0,getAM()));
resume_thread(tid_pager=spawn_thread(pagerThread,"pagerThread",0,getAM())); resume_thread(tid_pager=spawn_thread(pagerThread,"pagerThread",0,getAM()));
} }
// Find an area from an address that it contains
int vmInterface::getAreaByAddress(void *address) int vmInterface::getAreaByAddress(void *address)
{ {
int retVal; int retVal;

View File

@ -1,18 +1,75 @@
#include <vnodeManager.h> #include <vnodeManager.h>
#include <vnodePool.h>
vpage *vnodeManager::findVnode(vnode &target) // Functions for hash and isEqual
{ ulong mnHash (node &vnodule) { return ((vnode &)vnodule).fd; }
managedVnode *found=vnodes.find(&target);
bool mnIsEqual (node &vnodule1,node &vnodule2) {
vnode &v1= ((vnode &)vnodule1);
vnode &v2= ((vnode &)vnodule2);
return v1.fd==v2.fd && v1.offset==v2.offset;
}
// Set the hash and isEqual functions
vnodeManager::vnodeManager(void) : vnodes(20) {
vnodes.setHash(mnHash);
vnodes.setIsEqual(mnIsEqual);
}
// Find the vnode and return the first page that points to it.
vpage *vnodeManager::findVnode(vnode &target) {
vnode *found=reinterpret_cast<vnode *>(vnodes.find(&target));
if (found==NULL) if (found==NULL)
return NULL; return NULL;
else else
return found->pages.peek(); return reinterpret_cast<vpage *>(found->vpages.top());
} }
void vnodeManager::addVnode(vnode &target,vpage &vp) // If this vnode is already in use, add this vpage to it and return one to clone. If not, add this vnode, with this vpage, and return null
{ // This method will make a new vnode object
// Allocate space for a managed vnode vpage *vnodeManager::addVnode(vnode &target,vpage &vp) {
managedVnode *mv = NULL; // Fill this in later - maybe another pool? vpage *retVal;
mv->node=&target; error ("vnodeManager::addVnode : Adding by reference node %x, fd = %d, offset = %d\n",&target,target.fd,target.offset);
mv->pages.add(&vp); vnode *found=reinterpret_cast<vnode *>(vnodes.find(&target));
if (!found) {
found=new (vmBlock->vnodePool->get()) vnode;
found->fd=target.fd;
found->offset=target.offset;
vnodes.add(found);
retVal=NULL;
}
else
retVal=reinterpret_cast<vpage *>(found->vpages.top());
found->vpages.add(&vp);
return retVal;
}
// If this vnode is already in use, add this vpage to it and return one to clone. If not, add this vnode, with this vpage, and return null
// This method will NOT make a new vnode object
vpage *vnodeManager::addVnode(vnode *target,vpage &vp) {
vpage *retVal;
error ("vnodeManager::addVnode : Adding by pointer node %x, fd = %d, offset = %d\n",target,target->fd,target->offset);
vnode *found=reinterpret_cast<vnode *>(vnodes.find(target));
if (!found) {
found=target;
vnodes.add(found);
retVal=NULL;
}
else
retVal=reinterpret_cast<vpage *>(found->vpages.top());
found->vpages.add(&vp);
found->vpages.dump();
vnodes.dump();
}
// Remove a vpage from the manager; return "is this the last one"
bool vnodeManager::remove(vnode &target,vpage &vp) {
error ("vnodeManager::remove : Removing by reference node %x, fd = %d, offset = %d\n",&target,target.fd,target.offset);
vnode *found=reinterpret_cast<vnode *>(vnodes.find(&target));
if (!found) {
vnodes.dump();
throw ("An attempt to remove from an unknown vnode occured!\n");
}
found->vpages.remove(&vp);
return (found->vpages.count()==0);
} }

View File

@ -1,33 +1,21 @@
#ifndef VNODE_MANAGER
#define VNODE_MANAGER
#include <vpage.h>
#include <hashTable.h> #include <hashTable.h>
// vnode manager tracks which vnodes are already mapped to pages // vnode manager tracks which vnodes are already mapped to pages
// and facilitates sharing of memory containing the same disk space. // and facilitates sharing of memory containing the same disk space.
struct managedVnode
{
vnode *node;
list pages; // Hold the list of vpages that this vnode points to
};
ulong mnHash (node &vnodule) { return ((managedNode &)vnodule).node->fd; }
bool mnIsEqual (node &vnodule1,&vnodule2) {
managedNode &v1= ((managedNode &)vnodule1);
managedNode &v2= ((managedNode &)vnodule2);
return v1.node->fd==v2.node->fd && v1.node->offset==v2.node->offset;
}
class vnodeManager class vnodeManager
{ {
public: public:
vnodeManager(void) : vnodes(20) vnodeManager(void);
{
vnodes.setHash(mnHash);
vnodes.setIsEqual(mnIsEqual);
}
vpage *findVnode(vnode &target); // pass in a vnode, get back the "master" vpage vpage *findVnode(vnode &target); // pass in a vnode, get back the "master" vpage
void addVnode (vnode &target, vpage &vp); vpage *addVnode (vnode &target, vpage &vp);
vpage *addVnode(vnode *target,vpage &vp);
bool remove(vnode &target,vpage &vp);
private: private:
hashTable vnodes; hashTable vnodes;
} };
#endif

View File

@ -2,12 +2,14 @@
#include "vnodePool.h" #include "vnodePool.h"
#include "vmHeaderBlock.h" #include "vmHeaderBlock.h"
#include "areaManager.h" #include "areaManager.h"
#include "vnodeManager.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
extern vmHeaderBlock *vmBlock; extern vmHeaderBlock *vmBlock;
// Write this vpage out if necessary
void vpage::flush(void) { void vpage::flush(void) {
if (protection==writable && dirty) { if (protection==writable && dirty) {
//error (vpage::write_block: writing, backingNode->fd = %d, backingNode->offset = %d, address = %x\n",backingNode->fd, backingNode->offset,loc); //error (vpage::write_block: writing, backingNode->fd = %d, backingNode->offset = %d, address = %x\n",backingNode->fd, backingNode->offset,loc);
@ -20,6 +22,7 @@ void vpage::flush(void) {
} }
} }
// Load this vpage in if necessary
void vpage::refresh(void) { void vpage::refresh(void) {
if (backingNode->valid==false) if (backingNode->valid==false)
return; // Do nothing. This prevents "garbage" data on disk from being read in... return; // Do nothing. This prevents "garbage" data on disk from being read in...
@ -30,52 +33,68 @@ void vpage::refresh(void) {
error ("vpage::refresh: failed, fd = %d, errno = %d, %s\n",backingNode->fd,errno,strerror(errno)); error ("vpage::refresh: failed, fd = %d, errno = %d, %s\n",backingNode->fd,errno,strerror(errno));
} }
// Simple, empty constructor
vpage::vpage(void) : physPage(NULL),backingNode(NULL),protection(none),dirty(false),swappable(false),start_address(0),end_address(0) vpage::vpage(void) : physPage(NULL),backingNode(NULL),protection(none),dirty(false),swappable(false),start_address(0),end_address(0)
{ {
} }
// Does the real setup work for making a vpage.
// backing and/or physMem can be NULL/0. // backing and/or physMem can be NULL/0.
void vpage::setup(unsigned long start,vnode *backing, page *physMem,protectType prot,pageState state) { void vpage::setup(unsigned long start,vnode *backing, page *physMem,protectType prot,pageState state) {
// Basic setup from parameters
vpage *clonedPage; // This is the page that this page is to be the clone of...
error ("vpage::setup: start = %x, vnode.fd=%d, vnode.offset=%d, physMem = %x\n",start,((backing)?backing->fd:0),((backing)?backing->offset:0), ((physMem)?(physMem->getAddress()):0)); error ("vpage::setup: start = %x, vnode.fd=%d, vnode.offset=%d, physMem = %x\n",start,((backing)?backing->fd:0),((backing)?backing->offset:0), ((physMem)?(physMem->getAddress()):0));
physPage=physMem;
backingNode=backing;
protection=prot;
dirty=false;
swappable=(state==NO_LOCK);
start_address=start; start_address=start;
end_address=start+PAGE_SIZE-1; end_address=start+PAGE_SIZE-1;
protection=prot;
swappable=(state==NO_LOCK);
if (backing) { // Set up the backing store. If one is specified, use it; if not, get a swap file page.
backingNode=backing; if (backingNode) {
atomic_add(&(backing->count),1); clonedPage=vmBlock->vnodeMan->addVnode(*backingNode,*this); // Use the reference version which will make a new one if this one is not found
if (clonedPage) {
physPage=clonedPage->physPage;
protection=(protection<=readable)?protection: copyOnWrite;
if (clonedPage->getProtection()<=readable)
clonedPage->setProtection(copyOnWrite);
}
} }
else else { // Going to swap file.
backingNode=&(vmBlock->swapMan->findNode()); backingNode=&(vmBlock->swapMan->findNode());
if (!physMem && (state!=LAZY) && (state!=NO_LOCK)) { clonedPage=vmBlock->vnodeMan->addVnode(backingNode,*this); // Use the pointer version which will use this one. Should always return NULL
}
// If there is no physical page already and we can't wait to get one, then get one now
if (!physPage && (state!=LAZY) && (state!=NO_LOCK)) {
physPage=vmBlock->pageMan->getPage(); physPage=vmBlock->pageMan->getPage();
error ("vpage::setup, state = %d, allocated page %x\n",state,physPage); error ("vpage::setup, state = %d, allocated page %x\n",state,physPage);
} }
else { else { // We either don't need it or we already have it.
if (physMem) if (physPage)
atomic_add(&(physMem->count),1); atomic_add(&(physPage->count),1);
physPage=physMem;
} }
dirty=false;
error ("vpage::setup: ended : start = %x, vnode.fd=%d, vnode.offset=%d, physMem = %x\n",start,((backing)?backing->fd:0),((backing)?backing->offset:0), ((physMem)?(physMem->getAddress()):0)); error ("vpage::setup: ended : start = %x, vnode.fd=%d, vnode.offset=%d, physMem = %x\n",start,((backing)?backing->fd:0),((backing)?backing->offset:0), ((physMem)?(physMem->getAddress()):0));
} }
// Destruction.
void vpage::cleanup(void) { void vpage::cleanup(void) {
if (physPage) { // Note that free means release one reference if (physPage) { // Note that free means release one reference
error ("vpage::cleanup, freeing physcal page %x\n",physPage); error ("vpage::cleanup, freeing physcal page %x\n",physPage);
vmBlock->pageMan->freePage(physPage); vmBlock->pageMan->freePage(physPage); // This does nothing if someone else is using the physical page
} }
if (backingNode) { if (backingNode) { // If no one else is using this vnode, wipe it out
if (backingNode->fd) if (vmBlock->vnodeMan->remove(*backingNode,*this))
if (backingNode->fd==vmBlock->swapMan->getFD()) if (backingNode->fd && (backingNode->fd==vmBlock->swapMan->getFD()))
vmBlock->swapMan->freeVNode(*backingNode); vmBlock->swapMan->freeVNode(*backingNode);
else else
if ( atomic_add(&(backingNode->count),-1)==1) vmBlock->vnodePool->put(backingNode);
vmBlock->vnodePool->put(backingNode);
} }
} }
// Change this pages protection
void vpage::setProtection(protectType prot) { void vpage::setProtection(protectType prot) {
protection=prot; protection=prot;
// Change the hardware // Change the hardware
@ -85,7 +104,7 @@ void vpage::setProtection(protectType prot) {
// true = OK, false = panic. // true = OK, false = panic.
bool vpage::fault(void *fault_address, bool writeError) { bool vpage::fault(void *fault_address, bool writeError) {
error ("vpage::fault: virtual address = %lx, write = %s\n",(unsigned long) fault_address,((writeError)?"true":"false")); error ("vpage::fault: virtual address = %lx, write = %s\n",(unsigned long) fault_address,((writeError)?"true":"false"));
if (writeError && physPage) { if (writeError && physPage) { // If we already have a page and this is a write, it is either a copy on write or a "dirty" notice
dirty=true; dirty=true;
if (protection==copyOnWrite) { // Else, this was just a "let me know when I am dirty"... if (protection==copyOnWrite) { // Else, this was just a "let me know when I am dirty"...
page *newPhysPage=vmBlock->pageMan->getPage(); page *newPhysPage=vmBlock->pageMan->getPage();
@ -98,6 +117,7 @@ bool vpage::fault(void *fault_address, bool writeError) {
} }
return true; return true;
} }
// Guess this is the real deal. Get a physical page.
physPage=vmBlock->pageMan->getPage(); physPage=vmBlock->pageMan->getPage();
error ("vpage::fault - regular - allocated page %x\n",physPage); error ("vpage::fault - regular - allocated page %x\n",physPage);
if (!physPage) // No room at the inn if (!physPage) // No room at the inn
@ -149,6 +169,7 @@ void vpage::setInt(unsigned long address,int value,areaManager *manager) {
*((int *)(address-start_address+physPage->getAddress()))=value; *((int *)(address-start_address+physPage->getAddress()))=value;
} }
// Swaps pages out where necessary.
void vpage::pager(int desperation) { void vpage::pager(int desperation) {
//error ("vpage::pager start desperation = %d\n",desperation); //error ("vpage::pager start desperation = %d\n",desperation);
if (!swappable) if (!swappable)
@ -170,6 +191,7 @@ void vpage::pager(int desperation) {
physPage=NULL; physPage=NULL;
} }
// Saves dirty pages
void vpage::saver(void) { void vpage::saver(void) {
if (dirty) if (dirty)
flush(); flush();