Files
haiku-beta6/src/kernel/vm2/vpage.C
T
Michael Phipps d50d9a6a18 More code cleanup. Removing unused variables. Simplifying and shrinking
vpage to be nearly a TLB entry. Final stripping will occur with kernel
integration. More comments.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3042 a95241bf-73f2-0310-859d-f6bbb57e9c96
2003-03-26 04:00:38 +00:00

251 lines
10 KiB
C++

#include "vpage.h"
#include "vnodePool.h"
#include "vmHeaderBlock.h"
#include "areaManager.h"
#include "vnodeManager.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
extern vmHeaderBlock *vmBlock;
// Write this vpage out if necessary
void vpage::flush(void) {
if (physPage && getProtection()==writable && isDirty()) {
// error ("vpage::write_block: writing, backingNode->fd = %d, backingNode->offset = %d, address = %x\n",backingNode->fd, backingNode->offset,physPage->getAddress());
if (-1==lseek(backingNode->fd,backingNode->offset,SEEK_SET))
error ("vpage::flush:seek failed, fd = %d, errno = %d, %s\n",backingNode->fd,errno,strerror(errno));
if (-1==write(backingNode->fd,(void *)(physPage->getAddress()),PAGE_SIZE))
error ("vpage::flush: failed address =%x, fd = %d, offset = %d, errno = %d, %s\n",
start_address,backingNode->fd, backingNode->offset, errno,strerror(errno));
backingNode->valid=true;
//error ("vpage::write_block: done, backingNode->fd = %d, backingNode->offset = %d, address = %x\n",backingNode->fd, backingNode->offset,loc);
}
}
// Load this vpage in if necessary
void vpage::refresh(void) {
// error ("vpage::refresh: reading into %x\n",physPage->getAddress());
// backingNode->dump();
if (backingNode->valid==false)
return; // Do nothing. This prevents "garbage" data on disk from being read in...
if (-1==lseek(backingNode->fd,backingNode->offset,SEEK_SET))
error ("vpage::refresh: seek failed, fd = %d, errno = %d, %s\n",backingNode->fd,errno,strerror(errno));
if (-1==read(backingNode->fd,(void *)(physPage->getAddress()),PAGE_SIZE))
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),start_address(0),bits(0)
{
}
// Does the real setup work for making a vpage.
// backing and/or physMem can be NULL/0.
void vpage::setup(unsigned long start,vnode *backing, page *physMem,protectType prot,pageState state, mmapSharing share) {
// 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));
physPage=physMem;
backingNode=backing;
setProtection(prot);
error ("vpage::setup: fields, part 1 set\n");
dirty(false);
swappable(state==NO_LOCK);
locked(state==NO_LOCK);
start_address=start;
error ("vpage::setup: fields, part 2 set\n");
// Set up the backing store. If one is specified, use it; if not, get a swap file page.
if (backingNode) { // This is an mmapped file (or a cloned area)
switch (share) {
case CLONE: // This is a cloned area
case SHARED: // This is a shared mmap
clonedPage=vmBlock->vnodeMan->addVnode(*backingNode,*this,&backingNode); // Use the reference version which will make a new one if this one is not found
if (clonedPage) physPage=clonedPage->physPage;
break;
case PRIVATE: // This is a one way share - we get others changes (until we make a change) but no one gets our changes
clonedPage=vmBlock->vnodeMan->addVnode(*backingNode,*this,&backingNode); // Use the reference version which will make a new one if this one is not found
if (clonedPage) physPage=clonedPage->physPage;
setProtection((getProtection()<=readable)?getProtection(): copyOnWrite);
break;
case COPY: // This is not shared - get a fresh page and fresh swap file space and copy the original page
physPage=vmBlock->pageMan->getPage();
clonedPage=vmBlock->vnodeMan->findVnode(*backing); // Find out if the page we are copying is in memory already...
if (clonedPage && clonedPage->physPage) // If it is in memory, copy its values
memcpy((void *)(physPage->getAddress()),(void *)(clonedPage->physPage->getAddress()),PAGE_SIZE);
else
refresh(); // otherwise, get a copy from disk...
backingNode=&(vmBlock->swapMan->findNode()); // Now get swap space (since we don't want to be backed by the file...
clonedPage=vmBlock->vnodeMan->addVnode(backingNode,*this); // Add this vnode to the vnode keeper
break;
}
}
else { // Going to swap file.
backingNode=&(vmBlock->swapMan->findNode());
clonedPage=vmBlock->vnodeMan->addVnode(backingNode,*this); // Use the pointer version which will use this one. Should always return NULL
}
error ("vpage::setup: Backing node set up\n");
// 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();
error ("vpage::setup, state = %d, allocated page %x\n",state,physPage);
}
else { // We either don't need it or we already have it.
if (physPage)
atomic_add(&(physPage->count),1);
}
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) {
if (physPage) { // Note that free means release one reference
//error ("vpage::cleanup, freeing physcal page %x\n",physPage);
vmBlock->pageMan->freePage(physPage); // This does nothing if someone else is using the physical page
}
if (backingNode) { // If no one else is using this vnode, wipe it out
if (vmBlock->vnodeMan->remove(*backingNode,*this))
if (backingNode->fd && (backingNode->fd==vmBlock->swapMan->getFD()))
vmBlock->swapMan->freeVNode(*backingNode);
else
vmBlock->vnodePool->put(backingNode);
}
}
// Change this pages protection
void vpage::setProtection(protectType prot) {
protection(prot);
// Change the hardware
}
// This is dispatched by the real interrupt handler, who locates us
// true = OK, false = panic.
bool vpage::fault(void *fault_address, bool writeError, int &in_count) {
// error ("vpage::fault: virtual address = %lx, write = %s\n",(unsigned long) fault_address,((writeError)?"true":"false"));
if (writeError && getProtection() != copyOnWrite && getProtection() != writable)
return false;
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);
if (getProtection()==copyOnWrite) { // Else, this was just a "let me know when I am dirty"...
page *newPhysPage=vmBlock->pageMan->getPage();
// error ("vpage::fault - copy on write allocated page %x\n",newPhysPage);
memcpy((void *)(newPhysPage->getAddress()),(void *)(physPage->getAddress()),PAGE_SIZE);
physPage=newPhysPage;
setProtection(writable);
vmBlock->vnodeMan->remove(*backingNode,*this);
backingNode=&(vmBlock->swapMan->findNode()); // Need new backing store for this node, since it was copied, the original is no good...
vmBlock->vnodeMan->addVnode(backingNode,*this);
// Update the architecture specific stuff here...
}
return true;
}
// Guess this is the real deal. Get a physical page.
physPage=vmBlock->pageMan->getPage();
// error ("vpage::fault - regular - allocated page %x\n",physPage);
if (!physPage) // No room at the inn
return false;
// error ("vpage::fault: New page allocated! new physical address = %x vnode.fd=%d, vnode.offset=%d, \n",physPage->getAddress(),((backingNode)?backingNode->fd:0),((backingNode)?backingNode->offset:0));
// Update the architecture specific stuff here...
// This refresh is unneeded if the data was never written out...
// dump();
refresh(); // I wonder if these vnode calls are safe during an interrupt...
dirty(writeError); // If the client is writing, we are now dirty (or will be when we get back to user land)
in_count++;
//error ("vpage::fault: Refreshed\n");
// //dump();
//error ("vpage::fault: exiting\n");
return true;
}
bool vpage::lock(long flags) {
locked(true);
if (!physPage) {
physPage=vmBlock->pageMan->getPage();
if (!physPage)
return false;
refresh();
}
return true;
}
void vpage::unlock(long flags) {
if ((flags & B_DMA_IO) || (!(flags & B_READ_DEVICE)))
dirty(true);
locked(false);
}
char vpage::getByte(unsigned long address,areaManager *manager) {
if (!physPage)
if (!manager->fault((void *)(address),false))
throw ("vpage::getByte");
// error ("vpage::getByte: About to return %c from %x\n", *((char *)(address-start_address+physPage->getAddress())),(address-start_address+physPage->getAddress()));
return *((char *)(address-start_address+physPage->getAddress()));
}
void vpage::setByte(unsigned long address,char value,areaManager *manager) {
// error ("vpage::setByte: address = %d, value = %d\n",address, value);
if (!physPage)
if (!manager->fault((void *)(address),true))
throw ("vpage::setByte");
if (getProtection()>=writable)
*((char *)(address-start_address+physPage->getAddress()))=value;
else
throw ("vpage::setByte - no permission to write");
// error ("vpage::setByte: physical address = %d, value = %d\n",physPage->getAddress(), *((char *)(physPage->getAddress())));
}
int vpage::getInt(unsigned long address,areaManager *manager) {
// error ("vpage::getInt: address = %ld\n",address );
if (!physPage)
if (!manager->fault((void *)(address),false))
throw ("vpage::getInt");
//error ("vpage::getInt: About to return %d\n", *((char *)(address-start_address+physPage->getAddress())));
//dump();
return *((int *)(address-start_address+physPage->getAddress()));
}
void vpage::setInt(unsigned long address,int value,areaManager *manager) {
// error ("vpage::setInt: here I am!\n");
if (!physPage)
if (!manager->fault((void *)(address),true))
throw ("vpage::setInt");
if (getProtection()>=writable)
*((int *)(address-start_address+physPage->getAddress()))=value;
else
throw ("vpage::setInt - no permission to write");
// error ("vpage::setInt: leaving!\n");
}
// Swaps pages out where necessary.
bool vpage::pager(int desperation) {
//error ("vpage::pager start desperation = %d\n",desperation);
if (!isSwappable())
return false;
//error ("vpage::pager swappable\n");
switch (desperation) {
case 1: return false; break;
case 2: if (!physPage || getProtection()!=readable || isLocked()) return false;break;
case 3: if (!physPage || isDirty() || isLocked()) return false;break;
case 4: if (!physPage || isLocked()) return false;break;
case 5: if (!physPage || isLocked()) return false;break;
default: return false;break;
}
//error ("vpage::pager flushing\n");
flush();
//error ("vpage::pager freeing\n");
vmBlock->pageMan->freePage(physPage);
//error ("vpage::pager going to NULL\n");
physPage=NULL;
return true;
}
// Saves dirty pages
void vpage::saver(void) {
if (isDirty()) {
flush();
dirty(false);
}
}