Made VMAddressSpace an abstract base class and moved the area management into
new derived classes VM{Kernel,User}AddressSpace. Currently those are
identical, but that will change.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34492 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -23,8 +23,8 @@ public:
|
||||
|
||||
public:
|
||||
VMAddressSpace(team_id id, addr_t base,
|
||||
size_t size, bool kernel);
|
||||
~VMAddressSpace();
|
||||
size_t size, const char* name);
|
||||
virtual ~VMAddressSpace();
|
||||
|
||||
static status_t Init();
|
||||
static status_t InitPostSem();
|
||||
@@ -58,27 +58,32 @@ public:
|
||||
void IncrementChangeCount()
|
||||
{ fChangeCount++; }
|
||||
|
||||
VMArea* FirstArea() const;
|
||||
|
||||
VMArea* LookupArea(addr_t address) const;
|
||||
status_t InsertArea(void** _address, uint32 addressSpec,
|
||||
addr_t size, VMArea* area);
|
||||
void RemoveArea(VMArea* area);
|
||||
|
||||
bool CanResizeArea(VMArea* area, size_t newSize);
|
||||
status_t ResizeArea(VMArea* area, size_t newSize);
|
||||
status_t ResizeAreaHead(VMArea* area, size_t size);
|
||||
status_t ResizeAreaTail(VMArea* area, size_t size);
|
||||
|
||||
status_t ReserveAddressRange(void** _address,
|
||||
uint32 addressSpec, size_t size,
|
||||
uint32 flags);
|
||||
status_t UnreserveAddressRange(addr_t address,
|
||||
size_t size);
|
||||
void UnreserveAllAddressRanges();
|
||||
|
||||
inline AreaIterator GetAreaIterator();
|
||||
|
||||
VMAddressSpace*& HashTableLink() { return fHashTableLink; }
|
||||
|
||||
virtual VMArea* FirstArea() const = 0;
|
||||
virtual VMArea* NextArea(VMArea* area) const = 0;
|
||||
|
||||
virtual VMArea* LookupArea(addr_t address) const = 0;
|
||||
virtual status_t InsertArea(void** _address, uint32 addressSpec,
|
||||
addr_t size, VMArea* area) = 0;
|
||||
virtual void RemoveArea(VMArea* area) = 0;
|
||||
|
||||
virtual bool CanResizeArea(VMArea* area, size_t newSize) = 0;
|
||||
virtual status_t ResizeArea(VMArea* area, size_t newSize) = 0;
|
||||
virtual status_t ResizeAreaHead(VMArea* area, size_t size) = 0;
|
||||
virtual status_t ResizeAreaTail(VMArea* area, size_t size) = 0;
|
||||
|
||||
virtual status_t ReserveAddressRange(void** _address,
|
||||
uint32 addressSpec, size_t size,
|
||||
uint32 flags) = 0;
|
||||
virtual status_t UnreserveAddressRange(addr_t address,
|
||||
size_t size) = 0;
|
||||
virtual void UnreserveAllAddressRanges() = 0;
|
||||
|
||||
virtual void Dump() const;
|
||||
|
||||
static status_t Create(team_id teamID, addr_t base, size_t size,
|
||||
bool kernel,
|
||||
VMAddressSpace** _addressSpace);
|
||||
@@ -94,26 +99,14 @@ public:
|
||||
|
||||
static VMAddressSpace* Get(team_id teamID);
|
||||
|
||||
VMAddressSpace*& HashTableLink() { return fHashTableLink; }
|
||||
|
||||
void Dump() const;
|
||||
|
||||
private:
|
||||
status_t _InsertAreaIntoReservedRegion(addr_t start,
|
||||
size_t size, VMArea* area);
|
||||
status_t _InsertAreaSlot(addr_t start, addr_t size,
|
||||
addr_t end, uint32 addressSpec,
|
||||
VMArea* area);
|
||||
|
||||
protected:
|
||||
static int _DumpCommand(int argc, char** argv);
|
||||
static int _DumpListCommand(int argc, char** argv);
|
||||
|
||||
private:
|
||||
friend class Iterator;
|
||||
|
||||
protected:
|
||||
struct HashDefinition;
|
||||
|
||||
private:
|
||||
protected:
|
||||
VMAddressSpace* fHashTableLink;
|
||||
addr_t fBase;
|
||||
addr_t fEndAddress; // base + (size - 1)
|
||||
@@ -124,8 +117,6 @@ private:
|
||||
int32 fFaultCount;
|
||||
int32 fChangeCount;
|
||||
vm_translation_map fTranslationMap;
|
||||
VMAddressSpaceAreaList fAreas;
|
||||
mutable VMArea* fAreaHint;
|
||||
bool fDeleting;
|
||||
static VMAddressSpace* sKernelAddressSpace;
|
||||
};
|
||||
@@ -139,9 +130,9 @@ public:
|
||||
|
||||
AreaIterator(VMAddressSpace* addressSpace)
|
||||
:
|
||||
fIterator(addressSpace->fAreas.GetIterator())
|
||||
fAddressSpace(addressSpace),
|
||||
fNext(addressSpace->FirstArea())
|
||||
{
|
||||
_SkipReserved();
|
||||
}
|
||||
|
||||
bool HasNext() const
|
||||
@@ -152,40 +143,21 @@ public:
|
||||
VMArea* Next()
|
||||
{
|
||||
VMArea* result = fNext;
|
||||
_SkipReserved();
|
||||
fNext = fAddressSpace->NextArea(fNext);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Rewind()
|
||||
{
|
||||
fIterator.Rewind();
|
||||
_SkipReserved();
|
||||
fNext = fAddressSpace->FirstArea();
|
||||
}
|
||||
|
||||
private:
|
||||
void _SkipReserved()
|
||||
{
|
||||
while ((fNext = fIterator.Next()) != NULL
|
||||
&& fNext->id == RESERVED_AREA_ID) {
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VMAddressSpaceAreaList::Iterator fIterator;
|
||||
VMArea* fNext;
|
||||
VMAddressSpace* fAddressSpace;
|
||||
VMArea* fNext;
|
||||
};
|
||||
|
||||
|
||||
inline VMArea*
|
||||
VMAddressSpace::FirstArea() const
|
||||
{
|
||||
VMArea* area = fAreas.Head();
|
||||
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||
area = fAreas.GetNext(area);
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
inline VMAddressSpace::AreaIterator
|
||||
VMAddressSpace::GetAreaIterator()
|
||||
{
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
struct VMAddressSpace;
|
||||
struct VMCache;
|
||||
struct VMKernelAddressSpace;
|
||||
struct VMUserAddressSpace;
|
||||
|
||||
|
||||
struct VMArea {
|
||||
@@ -59,6 +61,8 @@ struct VMArea {
|
||||
|
||||
private:
|
||||
friend class VMAddressSpace;
|
||||
friend class VMKernelAddressSpace;
|
||||
friend class VMUserAddressSpace;
|
||||
|
||||
private:
|
||||
void SetBase(addr_t base) { fBase = base; }
|
||||
|
||||
@@ -15,8 +15,9 @@ KernelMergeObject kernel_vm.o :
|
||||
VMArea.cpp
|
||||
VMCache.cpp
|
||||
VMDeviceCache.cpp
|
||||
VMKernelAddressSpace.cpp
|
||||
VMNullCache.cpp
|
||||
#vm_tests.c
|
||||
VMUserAddressSpace.cpp
|
||||
|
||||
: $(TARGET_KERNEL_PIC_CCFLAGS)
|
||||
;
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#include <vm/vm.h>
|
||||
#include <vm/VMArea.h>
|
||||
|
||||
#include "VMKernelAddressSpace.h"
|
||||
#include "VMUserAddressSpace.h"
|
||||
|
||||
|
||||
//#define TRACE_VM
|
||||
#ifdef TRACE_VM
|
||||
@@ -33,17 +36,6 @@
|
||||
#define ASPACE_HASH_TABLE_SIZE 1024
|
||||
|
||||
|
||||
/*! Verifies that an area with the given aligned base and size fits into
|
||||
the spot defined by base and limit and checks for overflows.
|
||||
*/
|
||||
static inline bool
|
||||
is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
|
||||
{
|
||||
return (alignedBase >= base && alignedBase + (size - 1) > alignedBase
|
||||
&& alignedBase + (size - 1) <= limit);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - AddressSpaceHashDefinition
|
||||
|
||||
|
||||
@@ -84,7 +76,7 @@ VMAddressSpace* VMAddressSpace::sKernelAddressSpace;
|
||||
|
||||
|
||||
VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size,
|
||||
bool kernel)
|
||||
const char* name)
|
||||
:
|
||||
fBase(base),
|
||||
fEndAddress(base + (size - 1)),
|
||||
@@ -93,18 +85,15 @@ VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size,
|
||||
fRefCount(1),
|
||||
fFaultCount(0),
|
||||
fChangeCount(0),
|
||||
fAreaHint(NULL),
|
||||
fDeleting(false)
|
||||
{
|
||||
rw_lock_init(&fLock, kernel ? "kernel address space" : "address space");
|
||||
rw_lock_init(&fLock, name);
|
||||
// rw_lock_init(&fLock, kernel ? "kernel address space" : "address space");
|
||||
}
|
||||
|
||||
|
||||
VMAddressSpace::~VMAddressSpace()
|
||||
{
|
||||
if (this == sKernelAddressSpace)
|
||||
panic("deleting the kernel aspace!\n");
|
||||
|
||||
TRACE(("VMAddressSpace::~VMAddressSpace: called on aspace %" B_PRId32 "\n",
|
||||
ID()));
|
||||
|
||||
@@ -192,12 +181,27 @@ VMAddressSpace::RemoveAndPut()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VMAddressSpace::Dump() const
|
||||
{
|
||||
kprintf("dump of address space at %p:\n", this);
|
||||
kprintf("id: 0x%lx\n", fID);
|
||||
kprintf("ref_count: %ld\n", fRefCount);
|
||||
kprintf("fault_count: %ld\n", fFaultCount);
|
||||
kprintf("translation_map: %p\n", &fTranslationMap);
|
||||
kprintf("base: 0x%lx\n", fBase);
|
||||
kprintf("end: 0x%lx\n", fEndAddress);
|
||||
kprintf("change_count: 0x%lx\n", fChangeCount);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel,
|
||||
VMAddressSpace** _addressSpace)
|
||||
{
|
||||
VMAddressSpace* addressSpace = new(nogrow) VMAddressSpace(teamID, base,
|
||||
size, kernel);
|
||||
VMAddressSpace* addressSpace = kernel
|
||||
? (VMAddressSpace*)new(nogrow) VMKernelAddressSpace(teamID, base, size)
|
||||
: (VMAddressSpace*)new(nogrow) VMUserAddressSpace(teamID, base, size);
|
||||
if (addressSpace == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
@@ -273,585 +277,6 @@ VMAddressSpace::Get(team_id teamID)
|
||||
}
|
||||
|
||||
|
||||
//! You must hold the address space's read lock.
|
||||
VMArea*
|
||||
VMAddressSpace::LookupArea(addr_t address) const
|
||||
{
|
||||
// check the area hint first
|
||||
if (fAreaHint != NULL && fAreaHint->ContainsAddress(address))
|
||||
return fAreaHint;
|
||||
|
||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
if (area->id == RESERVED_AREA_ID)
|
||||
continue;
|
||||
|
||||
if (area->ContainsAddress(address)) {
|
||||
fAreaHint = area;
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*! This inserts the area you pass into the address space.
|
||||
It will also set the "_address" argument to its base address when
|
||||
the call succeeds.
|
||||
You need to hold the VMAddressSpace write lock.
|
||||
*/
|
||||
status_t
|
||||
VMAddressSpace::InsertArea(void** _address, uint32 addressSpec, addr_t size,
|
||||
VMArea* area)
|
||||
{
|
||||
addr_t searchBase, searchEnd;
|
||||
status_t status;
|
||||
|
||||
switch (addressSpec) {
|
||||
case B_EXACT_ADDRESS:
|
||||
searchBase = (addr_t)*_address;
|
||||
searchEnd = (addr_t)*_address + (size - 1);
|
||||
break;
|
||||
|
||||
case B_BASE_ADDRESS:
|
||||
searchBase = (addr_t)*_address;
|
||||
searchEnd = fEndAddress;
|
||||
break;
|
||||
|
||||
case B_ANY_ADDRESS:
|
||||
case B_ANY_KERNEL_ADDRESS:
|
||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||
searchBase = fBase;
|
||||
// TODO: remove this again when vm86 mode is moved into the kernel
|
||||
// completely (currently needs a userland address space!)
|
||||
if (searchBase == USER_BASE)
|
||||
searchBase = USER_BASE_ANY;
|
||||
searchEnd = fEndAddress;
|
||||
break;
|
||||
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
status = _InsertAreaSlot(searchBase, size, searchEnd, addressSpec, area);
|
||||
if (status == B_OK) {
|
||||
*_address = (void*)area->Base();
|
||||
fFreeSpace -= area->Size();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
//! You must hold the address space's write lock.
|
||||
void
|
||||
VMAddressSpace::RemoveArea(VMArea* area)
|
||||
{
|
||||
fAreas.Remove(area);
|
||||
|
||||
if (area->id != RESERVED_AREA_ID) {
|
||||
IncrementChangeCount();
|
||||
fFreeSpace += area->Size();
|
||||
|
||||
if (area == fAreaHint)
|
||||
fAreaHint = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VMAddressSpace::CanResizeArea(VMArea* area, size_t newSize)
|
||||
{
|
||||
VMArea* next = fAreas.GetNext(area);
|
||||
addr_t newEnd = area->Base() + (newSize - 1);
|
||||
if (next == NULL) {
|
||||
if (fEndAddress >= newEnd)
|
||||
return true;
|
||||
} else {
|
||||
if (next->Base() > newEnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the area was created inside a reserved area, it can
|
||||
// also be resized in that area
|
||||
// TODO: if there is free space after the reserved area, it could
|
||||
// be used as well...
|
||||
if (next->id == RESERVED_AREA_ID && next->cache_offset <= area->Base()
|
||||
&& next->Base() + (next->Size() - 1) >= newEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMAddressSpace::ResizeArea(VMArea* area, size_t newSize)
|
||||
{
|
||||
addr_t newEnd = area->Base() + (newSize - 1);
|
||||
VMArea* next = fAreas.GetNext(area);
|
||||
if (next != NULL && next->Base() <= newEnd) {
|
||||
if (next->id != RESERVED_AREA_ID
|
||||
|| next->cache_offset > area->Base()
|
||||
|| next->Base() + (next->Size() - 1) < newEnd) {
|
||||
panic("resize situation for area %p has changed although we "
|
||||
"should have the address space lock", area);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// resize reserved area
|
||||
addr_t offset = area->Base() + newSize - next->Base();
|
||||
if (next->Size() <= offset) {
|
||||
RemoveArea(next);
|
||||
free(next);
|
||||
} else {
|
||||
status_t error = ResizeAreaHead(next, next->Size() - offset);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return ResizeAreaTail(area, newSize);
|
||||
// TODO: In case of error we should undo the change to the reserved
|
||||
// area.
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMAddressSpace::ResizeAreaHead(VMArea* area, size_t size)
|
||||
{
|
||||
size_t oldSize = area->Size();
|
||||
if (size == oldSize)
|
||||
return B_OK;
|
||||
|
||||
area->SetBase(area->Base() + oldSize - size);
|
||||
area->SetSize(size);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMAddressSpace::ResizeAreaTail(VMArea* area, size_t size)
|
||||
{
|
||||
size_t oldSize = area->Size();
|
||||
if (size == oldSize)
|
||||
return B_OK;
|
||||
|
||||
area->SetSize(size);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMAddressSpace::ReserveAddressRange(void** _address, uint32 addressSpec,
|
||||
size_t size, uint32 flags)
|
||||
{
|
||||
// check to see if this address space has entered DELETE state
|
||||
if (fDeleting) {
|
||||
// okay, someone is trying to delete this address space now, so we
|
||||
// can't insert the area, let's back out
|
||||
return B_BAD_TEAM_ID;
|
||||
}
|
||||
|
||||
VMArea* area = VMArea::CreateReserved(this, flags);
|
||||
if (area == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t status = InsertArea(_address, addressSpec, size, area);
|
||||
if (status != B_OK) {
|
||||
free(area);
|
||||
return status;
|
||||
}
|
||||
|
||||
area->cache_offset = area->Base();
|
||||
// we cache the original base address here
|
||||
|
||||
Get();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMAddressSpace::UnreserveAddressRange(addr_t address, size_t size)
|
||||
{
|
||||
// check to see if this address space has entered DELETE state
|
||||
if (fDeleting) {
|
||||
// okay, someone is trying to delete this address space now, so we can't
|
||||
// insert the area, so back out
|
||||
return B_BAD_TEAM_ID;
|
||||
}
|
||||
|
||||
// search area list and remove any matching reserved ranges
|
||||
addr_t endAddress = address + (size - 1);
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
// the area must be completely part of the reserved range
|
||||
if (area->Base() + (area->Size() - 1) > endAddress)
|
||||
break;
|
||||
if (area->id == RESERVED_AREA_ID && area->Base() >= (addr_t)address) {
|
||||
// remove reserved range
|
||||
RemoveArea(area);
|
||||
Put();
|
||||
free(area);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VMAddressSpace::UnreserveAllAddressRanges()
|
||||
{
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
if (area->id == RESERVED_AREA_ID) {
|
||||
RemoveArea(area);
|
||||
Put();
|
||||
free(area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VMAddressSpace::Dump() const
|
||||
{
|
||||
kprintf("dump of address space at %p:\n", this);
|
||||
kprintf("id: 0x%lx\n", fID);
|
||||
kprintf("ref_count: %ld\n", fRefCount);
|
||||
kprintf("fault_count: %ld\n", fFaultCount);
|
||||
kprintf("translation_map: %p\n", &fTranslationMap);
|
||||
kprintf("base: 0x%lx\n", fBase);
|
||||
kprintf("end: 0x%lx\n", fEndAddress);
|
||||
kprintf("change_count: 0x%lx\n", fChangeCount);
|
||||
kprintf("area_hint: %p\n", fAreaHint);
|
||||
|
||||
kprintf("area_list:\n");
|
||||
|
||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
kprintf(" area 0x%lx: ", area->id);
|
||||
kprintf("base_addr = 0x%lx ", area->Base());
|
||||
kprintf("size = 0x%lx ", area->Size());
|
||||
kprintf("name = '%s' ", area->name);
|
||||
kprintf("protection = 0x%lx\n", area->protection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Finds a reserved area that covers the region spanned by \a start and
|
||||
\a size, inserts the \a area into that region and makes sure that
|
||||
there are reserved regions for the remaining parts.
|
||||
*/
|
||||
status_t
|
||||
VMAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
|
||||
VMArea* area)
|
||||
{
|
||||
VMArea* next;
|
||||
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
(next = it.Next()) != NULL;) {
|
||||
if (next->Base() <= start
|
||||
&& next->Base() + (next->Size() - 1) >= start + (size - 1)) {
|
||||
// This area covers the requested range
|
||||
if (next->id != RESERVED_AREA_ID) {
|
||||
// but it's not reserved space, it's a real area
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (next == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
// Now we have to transfer the requested part of the reserved
|
||||
// range to the new area - and remove, resize or split the old
|
||||
// reserved area.
|
||||
|
||||
if (start == next->Base()) {
|
||||
// the area starts at the beginning of the reserved range
|
||||
fAreas.Insert(next, area);
|
||||
|
||||
if (size == next->Size()) {
|
||||
// the new area fully covers the reversed range
|
||||
fAreas.Remove(next);
|
||||
Put();
|
||||
free(next);
|
||||
} else {
|
||||
// resize the reserved range behind the area
|
||||
next->SetBase(next->Base() + size);
|
||||
next->SetSize(next->Size() - size);
|
||||
}
|
||||
} else if (start + size == next->Base() + next->Size()) {
|
||||
// the area is at the end of the reserved range
|
||||
fAreas.Insert(fAreas.GetNext(next), area);
|
||||
|
||||
// resize the reserved range before the area
|
||||
next->SetSize(start - next->Base());
|
||||
} else {
|
||||
// the area splits the reserved range into two separate ones
|
||||
// we need a new reserved area to cover this space
|
||||
VMArea* reserved = VMArea::CreateReserved(this, next->protection);
|
||||
if (reserved == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
Get();
|
||||
fAreas.Insert(fAreas.GetNext(next), reserved);
|
||||
fAreas.Insert(reserved, area);
|
||||
|
||||
// resize regions
|
||||
reserved->SetSize(next->Base() + next->Size() - start - size);
|
||||
next->SetSize(start - next->Base());
|
||||
reserved->SetBase(start + size);
|
||||
reserved->cache_offset = next->cache_offset;
|
||||
}
|
||||
|
||||
area->SetBase(start);
|
||||
area->SetSize(size);
|
||||
IncrementChangeCount();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Must be called with this address space's write lock held */
|
||||
status_t
|
||||
VMAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end,
|
||||
uint32 addressSpec, VMArea* area)
|
||||
{
|
||||
VMArea* last = NULL;
|
||||
VMArea* next;
|
||||
bool foundSpot = false;
|
||||
|
||||
TRACE(("VMAddressSpace::InsertAreaSlot: address space %p, start 0x%lx, "
|
||||
"size %ld, end 0x%lx, addressSpec %ld, area %p\n", this, start,
|
||||
size, end, addressSpec, area));
|
||||
|
||||
// do some sanity checking
|
||||
if (start < fBase || size == 0 || end > fEndAddress
|
||||
|| start + (size - 1) > end)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (addressSpec == B_EXACT_ADDRESS && area->id != RESERVED_AREA_ID) {
|
||||
// search for a reserved area
|
||||
status_t status = _InsertAreaIntoReservedRegion(start, size, area);
|
||||
if (status == B_OK || status == B_BAD_VALUE)
|
||||
return status;
|
||||
|
||||
// There was no reserved area, and the slot doesn't seem to be used
|
||||
// already
|
||||
// TODO: this could be further optimized.
|
||||
}
|
||||
|
||||
size_t alignment = B_PAGE_SIZE;
|
||||
if (addressSpec == B_ANY_KERNEL_BLOCK_ADDRESS) {
|
||||
// align the memory to the next power of two of the size
|
||||
while (alignment < size)
|
||||
alignment <<= 1;
|
||||
}
|
||||
|
||||
start = ROUNDUP(start, alignment);
|
||||
|
||||
// walk up to the spot where we should start searching
|
||||
second_chance:
|
||||
VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
while ((next = it.Next()) != NULL) {
|
||||
if (next->Base() > start + (size - 1)) {
|
||||
// we have a winner
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
}
|
||||
|
||||
// find the right spot depending on the address specification - the area
|
||||
// will be inserted directly after "last" ("next" is not referenced anymore)
|
||||
|
||||
switch (addressSpec) {
|
||||
case B_ANY_ADDRESS:
|
||||
case B_ANY_KERNEL_ADDRESS:
|
||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||
{
|
||||
// find a hole big enough for a new area
|
||||
if (last == NULL) {
|
||||
// see if we can build it at the beginning of the virtual map
|
||||
addr_t alignedBase = ROUNDUP(fBase, alignment);
|
||||
if (is_valid_spot(fBase, alignedBase, size,
|
||||
next == NULL ? end : next->Base())) {
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
// keep walking
|
||||
while (next != NULL) {
|
||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||
alignment);
|
||||
if (is_valid_spot(last->Base() + (last->Size() - 1),
|
||||
alignedBase, size, next->Base())) {
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
if (foundSpot)
|
||||
break;
|
||||
|
||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||
alignment);
|
||||
if (is_valid_spot(last->Base() + (last->Size() - 1), alignedBase,
|
||||
size, end)) {
|
||||
// got a spot
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
} else if (area->id != RESERVED_AREA_ID) {
|
||||
// We didn't find a free spot - if there are any reserved areas,
|
||||
// we can now test those for free space
|
||||
// TODO: it would make sense to start with the biggest of them
|
||||
it.Rewind();
|
||||
next = it.Next();
|
||||
for (last = NULL; next != NULL; next = it.Next()) {
|
||||
if (next->id != RESERVED_AREA_ID) {
|
||||
last = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: take free space after the reserved area into
|
||||
// account!
|
||||
addr_t alignedBase = ROUNDUP(next->Base(), alignment);
|
||||
if (next->Base() == alignedBase && next->Size() == size) {
|
||||
// The reserved area is entirely covered, and thus,
|
||||
// removed
|
||||
fAreas.Remove(next);
|
||||
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
free(next);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((next->protection & RESERVED_AVOID_BASE) == 0
|
||||
&& alignedBase == next->Base()
|
||||
&& next->Size() >= size) {
|
||||
// The new area will be placed at the beginning of the
|
||||
// reserved area and the reserved area will be offset
|
||||
// and resized
|
||||
foundSpot = true;
|
||||
next->SetBase(next->Base() + size);
|
||||
next->SetSize(next->Size() - size);
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_valid_spot(next->Base(), alignedBase, size,
|
||||
next->Base() + (next->Size() - 1))) {
|
||||
// The new area will be placed at the end of the
|
||||
// reserved area, and the reserved area will be resized
|
||||
// to make space
|
||||
alignedBase = ROUNDDOWN(
|
||||
next->Base() + next->Size() - size, alignment);
|
||||
|
||||
foundSpot = true;
|
||||
next->SetSize(alignedBase - next->Base());
|
||||
area->SetBase(alignedBase);
|
||||
last = next;
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case B_BASE_ADDRESS:
|
||||
{
|
||||
// find a hole big enough for a new area beginning with "start"
|
||||
if (last == NULL) {
|
||||
// see if we can build it at the beginning of the specified
|
||||
// start
|
||||
if (next == NULL || next->Base() > start + (size - 1)) {
|
||||
foundSpot = true;
|
||||
area->SetBase(start);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
// keep walking
|
||||
while (next != NULL) {
|
||||
if (next->Base() - (last->Base() + last->Size()) >= size) {
|
||||
// we found a spot (it'll be filled up below)
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
addr_t lastEnd = last->Base() + (last->Size() - 1);
|
||||
if (next != NULL || end - lastEnd >= size) {
|
||||
// got a spot
|
||||
foundSpot = true;
|
||||
if (lastEnd < start)
|
||||
area->SetBase(start);
|
||||
else
|
||||
area->SetBase(lastEnd + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
// we didn't find a free spot in the requested range, so we'll
|
||||
// try again without any restrictions
|
||||
start = fBase;
|
||||
addressSpec = B_ANY_ADDRESS;
|
||||
last = NULL;
|
||||
goto second_chance;
|
||||
}
|
||||
|
||||
case B_EXACT_ADDRESS:
|
||||
// see if we can create it exactly here
|
||||
if ((last == NULL || last->Base() + (last->Size() - 1) < start)
|
||||
&& (next == NULL || next->Base() > start + (size - 1))) {
|
||||
foundSpot = true;
|
||||
area->SetBase(start);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (!foundSpot)
|
||||
return addressSpec == B_EXACT_ADDRESS ? B_BAD_VALUE : B_NO_MEMORY;
|
||||
|
||||
area->SetSize(size);
|
||||
if (last)
|
||||
fAreas.Insert(fAreas.GetNext(last), area);
|
||||
else
|
||||
fAreas.Insert(fAreas.Head(), area);
|
||||
|
||||
IncrementChangeCount();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ int
|
||||
VMAddressSpace::_DumpCommand(int argc, char** argv)
|
||||
{
|
||||
@@ -889,10 +314,9 @@ VMAddressSpace::_DumpListCommand(int argc, char** argv)
|
||||
while (VMAddressSpace* space = it.Next()) {
|
||||
int32 areaCount = 0;
|
||||
off_t areaSize = 0;
|
||||
for (VMAddressSpaceAreaList::Iterator it = space->fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
if (area->id != RESERVED_AREA_ID
|
||||
&& area->cache->type != CACHE_TYPE_NULL) {
|
||||
for (VMAddressSpace::AreaIterator areaIt = space->GetAreaIterator();
|
||||
VMArea* area = areaIt.Next();) {
|
||||
if (area->cache->type != CACHE_TYPE_NULL) {
|
||||
areaCount++;
|
||||
areaSize += area->Size();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Copyright 2002-2009, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||
* Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
|
||||
|
||||
#include "VMKernelAddressSpace.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <heap.h>
|
||||
#include <thread.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/VMArea.h>
|
||||
|
||||
|
||||
//#define TRACE_VM
|
||||
#ifdef TRACE_VM
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
/*! Verifies that an area with the given aligned base and size fits into
|
||||
the spot defined by base and limit and checks for overflows.
|
||||
*/
|
||||
static inline bool
|
||||
is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
|
||||
{
|
||||
return (alignedBase >= base && alignedBase + (size - 1) > alignedBase
|
||||
&& alignedBase + (size - 1) <= limit);
|
||||
}
|
||||
|
||||
|
||||
VMKernelAddressSpace::VMKernelAddressSpace(team_id id, addr_t base, size_t size)
|
||||
:
|
||||
VMAddressSpace(id, base, size, "kernel address space"),
|
||||
fAreaHint(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
VMKernelAddressSpace::~VMKernelAddressSpace()
|
||||
{
|
||||
panic("deleting the kernel aspace!\n");
|
||||
}
|
||||
|
||||
|
||||
inline VMArea*
|
||||
VMKernelAddressSpace::FirstArea() const
|
||||
{
|
||||
VMArea* area = fAreas.Head();
|
||||
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||
area = fAreas.GetNext(area);
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
inline VMArea*
|
||||
VMKernelAddressSpace::NextArea(VMArea* area) const
|
||||
{
|
||||
area = fAreas.GetNext(area);
|
||||
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||
area = fAreas.GetNext(area);
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
//! You must hold the address space's read lock.
|
||||
VMArea*
|
||||
VMKernelAddressSpace::LookupArea(addr_t address) const
|
||||
{
|
||||
// check the area hint first
|
||||
if (fAreaHint != NULL && fAreaHint->ContainsAddress(address))
|
||||
return fAreaHint;
|
||||
|
||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
if (area->id == RESERVED_AREA_ID)
|
||||
continue;
|
||||
|
||||
if (area->ContainsAddress(address)) {
|
||||
fAreaHint = area;
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*! This inserts the area you pass into the address space.
|
||||
It will also set the "_address" argument to its base address when
|
||||
the call succeeds.
|
||||
You need to hold the VMAddressSpace write lock.
|
||||
*/
|
||||
status_t
|
||||
VMKernelAddressSpace::InsertArea(void** _address, uint32 addressSpec,
|
||||
addr_t size, VMArea* area)
|
||||
{
|
||||
addr_t searchBase, searchEnd;
|
||||
status_t status;
|
||||
|
||||
switch (addressSpec) {
|
||||
case B_EXACT_ADDRESS:
|
||||
searchBase = (addr_t)*_address;
|
||||
searchEnd = (addr_t)*_address + (size - 1);
|
||||
break;
|
||||
|
||||
case B_BASE_ADDRESS:
|
||||
searchBase = (addr_t)*_address;
|
||||
searchEnd = fEndAddress;
|
||||
break;
|
||||
|
||||
case B_ANY_ADDRESS:
|
||||
case B_ANY_KERNEL_ADDRESS:
|
||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||
searchBase = fBase;
|
||||
// TODO: remove this again when vm86 mode is moved into the kernel
|
||||
// completely (currently needs a userland address space!)
|
||||
if (searchBase == USER_BASE)
|
||||
searchBase = USER_BASE_ANY;
|
||||
searchEnd = fEndAddress;
|
||||
break;
|
||||
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
status = _InsertAreaSlot(searchBase, size, searchEnd, addressSpec, area);
|
||||
if (status == B_OK) {
|
||||
*_address = (void*)area->Base();
|
||||
fFreeSpace -= area->Size();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
//! You must hold the address space's write lock.
|
||||
void
|
||||
VMKernelAddressSpace::RemoveArea(VMArea* area)
|
||||
{
|
||||
fAreas.Remove(area);
|
||||
|
||||
if (area->id != RESERVED_AREA_ID) {
|
||||
IncrementChangeCount();
|
||||
fFreeSpace += area->Size();
|
||||
|
||||
if (area == fAreaHint)
|
||||
fAreaHint = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VMKernelAddressSpace::CanResizeArea(VMArea* area, size_t newSize)
|
||||
{
|
||||
VMArea* next = fAreas.GetNext(area);
|
||||
addr_t newEnd = area->Base() + (newSize - 1);
|
||||
if (next == NULL) {
|
||||
if (fEndAddress >= newEnd)
|
||||
return true;
|
||||
} else {
|
||||
if (next->Base() > newEnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the area was created inside a reserved area, it can
|
||||
// also be resized in that area
|
||||
// TODO: if there is free space after the reserved area, it could
|
||||
// be used as well...
|
||||
if (next->id == RESERVED_AREA_ID && next->cache_offset <= area->Base()
|
||||
&& next->Base() + (next->Size() - 1) >= newEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMKernelAddressSpace::ResizeArea(VMArea* area, size_t newSize)
|
||||
{
|
||||
addr_t newEnd = area->Base() + (newSize - 1);
|
||||
VMArea* next = fAreas.GetNext(area);
|
||||
if (next != NULL && next->Base() <= newEnd) {
|
||||
if (next->id != RESERVED_AREA_ID
|
||||
|| next->cache_offset > area->Base()
|
||||
|| next->Base() + (next->Size() - 1) < newEnd) {
|
||||
panic("resize situation for area %p has changed although we "
|
||||
"should have the address space lock", area);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// resize reserved area
|
||||
addr_t offset = area->Base() + newSize - next->Base();
|
||||
if (next->Size() <= offset) {
|
||||
RemoveArea(next);
|
||||
free(next);
|
||||
} else {
|
||||
status_t error = ResizeAreaHead(next, next->Size() - offset);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return ResizeAreaTail(area, newSize);
|
||||
// TODO: In case of error we should undo the change to the reserved
|
||||
// area.
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMKernelAddressSpace::ResizeAreaHead(VMArea* area, size_t size)
|
||||
{
|
||||
size_t oldSize = area->Size();
|
||||
if (size == oldSize)
|
||||
return B_OK;
|
||||
|
||||
area->SetBase(area->Base() + oldSize - size);
|
||||
area->SetSize(size);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMKernelAddressSpace::ResizeAreaTail(VMArea* area, size_t size)
|
||||
{
|
||||
size_t oldSize = area->Size();
|
||||
if (size == oldSize)
|
||||
return B_OK;
|
||||
|
||||
area->SetSize(size);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMKernelAddressSpace::ReserveAddressRange(void** _address, uint32 addressSpec,
|
||||
size_t size, uint32 flags)
|
||||
{
|
||||
// check to see if this address space has entered DELETE state
|
||||
if (fDeleting) {
|
||||
// okay, someone is trying to delete this address space now, so we
|
||||
// can't insert the area, let's back out
|
||||
return B_BAD_TEAM_ID;
|
||||
}
|
||||
|
||||
VMArea* area = VMArea::CreateReserved(this, flags);
|
||||
if (area == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t status = InsertArea(_address, addressSpec, size, area);
|
||||
if (status != B_OK) {
|
||||
free(area);
|
||||
return status;
|
||||
}
|
||||
|
||||
area->cache_offset = area->Base();
|
||||
// we cache the original base address here
|
||||
|
||||
Get();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMKernelAddressSpace::UnreserveAddressRange(addr_t address, size_t size)
|
||||
{
|
||||
// check to see if this address space has entered DELETE state
|
||||
if (fDeleting) {
|
||||
// okay, someone is trying to delete this address space now, so we can't
|
||||
// insert the area, so back out
|
||||
return B_BAD_TEAM_ID;
|
||||
}
|
||||
|
||||
// search area list and remove any matching reserved ranges
|
||||
addr_t endAddress = address + (size - 1);
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
// the area must be completely part of the reserved range
|
||||
if (area->Base() + (area->Size() - 1) > endAddress)
|
||||
break;
|
||||
if (area->id == RESERVED_AREA_ID && area->Base() >= (addr_t)address) {
|
||||
// remove reserved range
|
||||
RemoveArea(area);
|
||||
Put();
|
||||
free(area);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VMKernelAddressSpace::UnreserveAllAddressRanges()
|
||||
{
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
if (area->id == RESERVED_AREA_ID) {
|
||||
RemoveArea(area);
|
||||
Put();
|
||||
free(area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VMKernelAddressSpace::Dump() const
|
||||
{
|
||||
VMAddressSpace::Dump();
|
||||
kprintf("area_hint: %p\n", fAreaHint);
|
||||
|
||||
kprintf("area_list:\n");
|
||||
|
||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
kprintf(" area 0x%lx: ", area->id);
|
||||
kprintf("base_addr = 0x%lx ", area->Base());
|
||||
kprintf("size = 0x%lx ", area->Size());
|
||||
kprintf("name = '%s' ", area->name);
|
||||
kprintf("protection = 0x%lx\n", area->protection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Finds a reserved area that covers the region spanned by \a start and
|
||||
\a size, inserts the \a area into that region and makes sure that
|
||||
there are reserved regions for the remaining parts.
|
||||
*/
|
||||
status_t
|
||||
VMKernelAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
|
||||
VMArea* area)
|
||||
{
|
||||
VMArea* next;
|
||||
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
(next = it.Next()) != NULL;) {
|
||||
if (next->Base() <= start
|
||||
&& next->Base() + (next->Size() - 1) >= start + (size - 1)) {
|
||||
// This area covers the requested range
|
||||
if (next->id != RESERVED_AREA_ID) {
|
||||
// but it's not reserved space, it's a real area
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (next == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
// Now we have to transfer the requested part of the reserved
|
||||
// range to the new area - and remove, resize or split the old
|
||||
// reserved area.
|
||||
|
||||
if (start == next->Base()) {
|
||||
// the area starts at the beginning of the reserved range
|
||||
fAreas.Insert(next, area);
|
||||
|
||||
if (size == next->Size()) {
|
||||
// the new area fully covers the reversed range
|
||||
fAreas.Remove(next);
|
||||
Put();
|
||||
free(next);
|
||||
} else {
|
||||
// resize the reserved range behind the area
|
||||
next->SetBase(next->Base() + size);
|
||||
next->SetSize(next->Size() - size);
|
||||
}
|
||||
} else if (start + size == next->Base() + next->Size()) {
|
||||
// the area is at the end of the reserved range
|
||||
fAreas.Insert(fAreas.GetNext(next), area);
|
||||
|
||||
// resize the reserved range before the area
|
||||
next->SetSize(start - next->Base());
|
||||
} else {
|
||||
// the area splits the reserved range into two separate ones
|
||||
// we need a new reserved area to cover this space
|
||||
VMArea* reserved = VMArea::CreateReserved(this, next->protection);
|
||||
if (reserved == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
Get();
|
||||
fAreas.Insert(fAreas.GetNext(next), reserved);
|
||||
fAreas.Insert(reserved, area);
|
||||
|
||||
// resize regions
|
||||
reserved->SetSize(next->Base() + next->Size() - start - size);
|
||||
next->SetSize(start - next->Base());
|
||||
reserved->SetBase(start + size);
|
||||
reserved->cache_offset = next->cache_offset;
|
||||
}
|
||||
|
||||
area->SetBase(start);
|
||||
area->SetSize(size);
|
||||
IncrementChangeCount();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Must be called with this address space's write lock held */
|
||||
status_t
|
||||
VMKernelAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end,
|
||||
uint32 addressSpec, VMArea* area)
|
||||
{
|
||||
VMArea* last = NULL;
|
||||
VMArea* next;
|
||||
bool foundSpot = false;
|
||||
|
||||
TRACE(("VMKernelAddressSpace::_InsertAreaSlot: address space %p, start "
|
||||
"0x%lx, size %ld, end 0x%lx, addressSpec %ld, area %p\n", this, start,
|
||||
size, end, addressSpec, area));
|
||||
|
||||
// do some sanity checking
|
||||
if (start < fBase || size == 0 || end > fEndAddress
|
||||
|| start + (size - 1) > end)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (addressSpec == B_EXACT_ADDRESS && area->id != RESERVED_AREA_ID) {
|
||||
// search for a reserved area
|
||||
status_t status = _InsertAreaIntoReservedRegion(start, size, area);
|
||||
if (status == B_OK || status == B_BAD_VALUE)
|
||||
return status;
|
||||
|
||||
// There was no reserved area, and the slot doesn't seem to be used
|
||||
// already
|
||||
// TODO: this could be further optimized.
|
||||
}
|
||||
|
||||
size_t alignment = B_PAGE_SIZE;
|
||||
if (addressSpec == B_ANY_KERNEL_BLOCK_ADDRESS) {
|
||||
// align the memory to the next power of two of the size
|
||||
while (alignment < size)
|
||||
alignment <<= 1;
|
||||
}
|
||||
|
||||
start = ROUNDUP(start, alignment);
|
||||
|
||||
// walk up to the spot where we should start searching
|
||||
second_chance:
|
||||
VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
while ((next = it.Next()) != NULL) {
|
||||
if (next->Base() > start + (size - 1)) {
|
||||
// we have a winner
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
}
|
||||
|
||||
// find the right spot depending on the address specification - the area
|
||||
// will be inserted directly after "last" ("next" is not referenced anymore)
|
||||
|
||||
switch (addressSpec) {
|
||||
case B_ANY_ADDRESS:
|
||||
case B_ANY_KERNEL_ADDRESS:
|
||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||
{
|
||||
// find a hole big enough for a new area
|
||||
if (last == NULL) {
|
||||
// see if we can build it at the beginning of the virtual map
|
||||
addr_t alignedBase = ROUNDUP(fBase, alignment);
|
||||
if (is_valid_spot(fBase, alignedBase, size,
|
||||
next == NULL ? end : next->Base())) {
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
// keep walking
|
||||
while (next != NULL) {
|
||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||
alignment);
|
||||
if (is_valid_spot(last->Base() + (last->Size() - 1),
|
||||
alignedBase, size, next->Base())) {
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
if (foundSpot)
|
||||
break;
|
||||
|
||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||
alignment);
|
||||
if (is_valid_spot(last->Base() + (last->Size() - 1), alignedBase,
|
||||
size, end)) {
|
||||
// got a spot
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
} else if (area->id != RESERVED_AREA_ID) {
|
||||
// We didn't find a free spot - if there are any reserved areas,
|
||||
// we can now test those for free space
|
||||
// TODO: it would make sense to start with the biggest of them
|
||||
it.Rewind();
|
||||
next = it.Next();
|
||||
for (last = NULL; next != NULL; next = it.Next()) {
|
||||
if (next->id != RESERVED_AREA_ID) {
|
||||
last = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: take free space after the reserved area into
|
||||
// account!
|
||||
addr_t alignedBase = ROUNDUP(next->Base(), alignment);
|
||||
if (next->Base() == alignedBase && next->Size() == size) {
|
||||
// The reserved area is entirely covered, and thus,
|
||||
// removed
|
||||
fAreas.Remove(next);
|
||||
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
free(next);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((next->protection & RESERVED_AVOID_BASE) == 0
|
||||
&& alignedBase == next->Base()
|
||||
&& next->Size() >= size) {
|
||||
// The new area will be placed at the beginning of the
|
||||
// reserved area and the reserved area will be offset
|
||||
// and resized
|
||||
foundSpot = true;
|
||||
next->SetBase(next->Base() + size);
|
||||
next->SetSize(next->Size() - size);
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_valid_spot(next->Base(), alignedBase, size,
|
||||
next->Base() + (next->Size() - 1))) {
|
||||
// The new area will be placed at the end of the
|
||||
// reserved area, and the reserved area will be resized
|
||||
// to make space
|
||||
alignedBase = ROUNDDOWN(
|
||||
next->Base() + next->Size() - size, alignment);
|
||||
|
||||
foundSpot = true;
|
||||
next->SetSize(alignedBase - next->Base());
|
||||
area->SetBase(alignedBase);
|
||||
last = next;
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case B_BASE_ADDRESS:
|
||||
{
|
||||
// find a hole big enough for a new area beginning with "start"
|
||||
if (last == NULL) {
|
||||
// see if we can build it at the beginning of the specified
|
||||
// start
|
||||
if (next == NULL || next->Base() > start + (size - 1)) {
|
||||
foundSpot = true;
|
||||
area->SetBase(start);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
// keep walking
|
||||
while (next != NULL) {
|
||||
if (next->Base() - (last->Base() + last->Size()) >= size) {
|
||||
// we found a spot (it'll be filled up below)
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
addr_t lastEnd = last->Base() + (last->Size() - 1);
|
||||
if (next != NULL || end - lastEnd >= size) {
|
||||
// got a spot
|
||||
foundSpot = true;
|
||||
if (lastEnd < start)
|
||||
area->SetBase(start);
|
||||
else
|
||||
area->SetBase(lastEnd + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
// we didn't find a free spot in the requested range, so we'll
|
||||
// try again without any restrictions
|
||||
start = fBase;
|
||||
addressSpec = B_ANY_ADDRESS;
|
||||
last = NULL;
|
||||
goto second_chance;
|
||||
}
|
||||
|
||||
case B_EXACT_ADDRESS:
|
||||
// see if we can create it exactly here
|
||||
if ((last == NULL || last->Base() + (last->Size() - 1) < start)
|
||||
&& (next == NULL || next->Base() > start + (size - 1))) {
|
||||
foundSpot = true;
|
||||
area->SetBase(start);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (!foundSpot)
|
||||
return addressSpec == B_EXACT_ADDRESS ? B_BAD_VALUE : B_NO_MEMORY;
|
||||
|
||||
area->SetSize(size);
|
||||
if (last)
|
||||
fAreas.Insert(fAreas.GetNext(last), area);
|
||||
else
|
||||
fAreas.Insert(fAreas.Head(), area);
|
||||
|
||||
IncrementChangeCount();
|
||||
return B_OK;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Copyright 2002-2008, Axel Dörfler, [email protected]. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||
* Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
#ifndef VM_KERNEL_ADDRESS_SPACE_H
|
||||
#define VM_KERNEL_ADDRESS_SPACE_H
|
||||
|
||||
|
||||
#include <vm/VMAddressSpace.h>
|
||||
|
||||
|
||||
struct VMKernelAddressSpace : VMAddressSpace {
|
||||
public:
|
||||
VMKernelAddressSpace(team_id id, addr_t base,
|
||||
size_t size);
|
||||
virtual ~VMKernelAddressSpace();
|
||||
|
||||
virtual VMArea* FirstArea() const;
|
||||
virtual VMArea* NextArea(VMArea* area) const;
|
||||
|
||||
virtual VMArea* LookupArea(addr_t address) const;
|
||||
virtual status_t InsertArea(void** _address, uint32 addressSpec,
|
||||
addr_t size, VMArea* area);
|
||||
virtual void RemoveArea(VMArea* area);
|
||||
|
||||
virtual bool CanResizeArea(VMArea* area, size_t newSize);
|
||||
virtual status_t ResizeArea(VMArea* area, size_t newSize);
|
||||
virtual status_t ResizeAreaHead(VMArea* area, size_t size);
|
||||
virtual status_t ResizeAreaTail(VMArea* area, size_t size);
|
||||
|
||||
virtual status_t ReserveAddressRange(void** _address,
|
||||
uint32 addressSpec, size_t size,
|
||||
uint32 flags);
|
||||
virtual status_t UnreserveAddressRange(addr_t address,
|
||||
size_t size);
|
||||
virtual void UnreserveAllAddressRanges();
|
||||
|
||||
virtual void Dump() const;
|
||||
|
||||
private:
|
||||
status_t _InsertAreaIntoReservedRegion(addr_t start,
|
||||
size_t size, VMArea* area);
|
||||
status_t _InsertAreaSlot(addr_t start, addr_t size,
|
||||
addr_t end, uint32 addressSpec,
|
||||
VMArea* area);
|
||||
|
||||
private:
|
||||
VMAddressSpaceAreaList fAreas;
|
||||
mutable VMArea* fAreaHint;
|
||||
};
|
||||
|
||||
|
||||
#endif /* VM_KERNEL_ADDRESS_SPACE_H */
|
||||
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Copyright 2002-2009, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||
* Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
|
||||
|
||||
#include "VMUserAddressSpace.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <heap.h>
|
||||
#include <thread.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/VMArea.h>
|
||||
|
||||
|
||||
//#define TRACE_VM
|
||||
#ifdef TRACE_VM
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
/*! Verifies that an area with the given aligned base and size fits into
|
||||
the spot defined by base and limit and checks for overflows.
|
||||
*/
|
||||
static inline bool
|
||||
is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
|
||||
{
|
||||
return (alignedBase >= base && alignedBase + (size - 1) > alignedBase
|
||||
&& alignedBase + (size - 1) <= limit);
|
||||
}
|
||||
|
||||
|
||||
VMUserAddressSpace::VMUserAddressSpace(team_id id, addr_t base, size_t size)
|
||||
:
|
||||
VMAddressSpace(id, base, size, "kernel address space"),
|
||||
fAreaHint(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
VMUserAddressSpace::~VMUserAddressSpace()
|
||||
{
|
||||
panic("deleting the kernel aspace!\n");
|
||||
}
|
||||
|
||||
|
||||
inline VMArea*
|
||||
VMUserAddressSpace::FirstArea() const
|
||||
{
|
||||
VMArea* area = fAreas.Head();
|
||||
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||
area = fAreas.GetNext(area);
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
inline VMArea*
|
||||
VMUserAddressSpace::NextArea(VMArea* area) const
|
||||
{
|
||||
area = fAreas.GetNext(area);
|
||||
while (area != NULL && area->id == RESERVED_AREA_ID)
|
||||
area = fAreas.GetNext(area);
|
||||
return area;
|
||||
}
|
||||
|
||||
|
||||
//! You must hold the address space's read lock.
|
||||
VMArea*
|
||||
VMUserAddressSpace::LookupArea(addr_t address) const
|
||||
{
|
||||
// check the area hint first
|
||||
if (fAreaHint != NULL && fAreaHint->ContainsAddress(address))
|
||||
return fAreaHint;
|
||||
|
||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
if (area->id == RESERVED_AREA_ID)
|
||||
continue;
|
||||
|
||||
if (area->ContainsAddress(address)) {
|
||||
fAreaHint = area;
|
||||
return area;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*! This inserts the area you pass into the address space.
|
||||
It will also set the "_address" argument to its base address when
|
||||
the call succeeds.
|
||||
You need to hold the VMAddressSpace write lock.
|
||||
*/
|
||||
status_t
|
||||
VMUserAddressSpace::InsertArea(void** _address, uint32 addressSpec,
|
||||
addr_t size, VMArea* area)
|
||||
{
|
||||
addr_t searchBase, searchEnd;
|
||||
status_t status;
|
||||
|
||||
switch (addressSpec) {
|
||||
case B_EXACT_ADDRESS:
|
||||
searchBase = (addr_t)*_address;
|
||||
searchEnd = (addr_t)*_address + (size - 1);
|
||||
break;
|
||||
|
||||
case B_BASE_ADDRESS:
|
||||
searchBase = (addr_t)*_address;
|
||||
searchEnd = fEndAddress;
|
||||
break;
|
||||
|
||||
case B_ANY_ADDRESS:
|
||||
case B_ANY_KERNEL_ADDRESS:
|
||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||
searchBase = fBase;
|
||||
// TODO: remove this again when vm86 mode is moved into the kernel
|
||||
// completely (currently needs a userland address space!)
|
||||
if (searchBase == USER_BASE)
|
||||
searchBase = USER_BASE_ANY;
|
||||
searchEnd = fEndAddress;
|
||||
break;
|
||||
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
status = _InsertAreaSlot(searchBase, size, searchEnd, addressSpec, area);
|
||||
if (status == B_OK) {
|
||||
*_address = (void*)area->Base();
|
||||
fFreeSpace -= area->Size();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
//! You must hold the address space's write lock.
|
||||
void
|
||||
VMUserAddressSpace::RemoveArea(VMArea* area)
|
||||
{
|
||||
fAreas.Remove(area);
|
||||
|
||||
if (area->id != RESERVED_AREA_ID) {
|
||||
IncrementChangeCount();
|
||||
fFreeSpace += area->Size();
|
||||
|
||||
if (area == fAreaHint)
|
||||
fAreaHint = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VMUserAddressSpace::CanResizeArea(VMArea* area, size_t newSize)
|
||||
{
|
||||
VMArea* next = fAreas.GetNext(area);
|
||||
addr_t newEnd = area->Base() + (newSize - 1);
|
||||
if (next == NULL) {
|
||||
if (fEndAddress >= newEnd)
|
||||
return true;
|
||||
} else {
|
||||
if (next->Base() > newEnd)
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the area was created inside a reserved area, it can
|
||||
// also be resized in that area
|
||||
// TODO: if there is free space after the reserved area, it could
|
||||
// be used as well...
|
||||
if (next->id == RESERVED_AREA_ID && next->cache_offset <= area->Base()
|
||||
&& next->Base() + (next->Size() - 1) >= newEnd) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMUserAddressSpace::ResizeArea(VMArea* area, size_t newSize)
|
||||
{
|
||||
addr_t newEnd = area->Base() + (newSize - 1);
|
||||
VMArea* next = fAreas.GetNext(area);
|
||||
if (next != NULL && next->Base() <= newEnd) {
|
||||
if (next->id != RESERVED_AREA_ID
|
||||
|| next->cache_offset > area->Base()
|
||||
|| next->Base() + (next->Size() - 1) < newEnd) {
|
||||
panic("resize situation for area %p has changed although we "
|
||||
"should have the address space lock", area);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// resize reserved area
|
||||
addr_t offset = area->Base() + newSize - next->Base();
|
||||
if (next->Size() <= offset) {
|
||||
RemoveArea(next);
|
||||
free(next);
|
||||
} else {
|
||||
status_t error = ResizeAreaHead(next, next->Size() - offset);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return ResizeAreaTail(area, newSize);
|
||||
// TODO: In case of error we should undo the change to the reserved
|
||||
// area.
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMUserAddressSpace::ResizeAreaHead(VMArea* area, size_t size)
|
||||
{
|
||||
size_t oldSize = area->Size();
|
||||
if (size == oldSize)
|
||||
return B_OK;
|
||||
|
||||
area->SetBase(area->Base() + oldSize - size);
|
||||
area->SetSize(size);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMUserAddressSpace::ResizeAreaTail(VMArea* area, size_t size)
|
||||
{
|
||||
size_t oldSize = area->Size();
|
||||
if (size == oldSize)
|
||||
return B_OK;
|
||||
|
||||
area->SetSize(size);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMUserAddressSpace::ReserveAddressRange(void** _address, uint32 addressSpec,
|
||||
size_t size, uint32 flags)
|
||||
{
|
||||
// check to see if this address space has entered DELETE state
|
||||
if (fDeleting) {
|
||||
// okay, someone is trying to delete this address space now, so we
|
||||
// can't insert the area, let's back out
|
||||
return B_BAD_TEAM_ID;
|
||||
}
|
||||
|
||||
VMArea* area = VMArea::CreateReserved(this, flags);
|
||||
if (area == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t status = InsertArea(_address, addressSpec, size, area);
|
||||
if (status != B_OK) {
|
||||
free(area);
|
||||
return status;
|
||||
}
|
||||
|
||||
area->cache_offset = area->Base();
|
||||
// we cache the original base address here
|
||||
|
||||
Get();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMUserAddressSpace::UnreserveAddressRange(addr_t address, size_t size)
|
||||
{
|
||||
// check to see if this address space has entered DELETE state
|
||||
if (fDeleting) {
|
||||
// okay, someone is trying to delete this address space now, so we can't
|
||||
// insert the area, so back out
|
||||
return B_BAD_TEAM_ID;
|
||||
}
|
||||
|
||||
// search area list and remove any matching reserved ranges
|
||||
addr_t endAddress = address + (size - 1);
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
// the area must be completely part of the reserved range
|
||||
if (area->Base() + (area->Size() - 1) > endAddress)
|
||||
break;
|
||||
if (area->id == RESERVED_AREA_ID && area->Base() >= (addr_t)address) {
|
||||
// remove reserved range
|
||||
RemoveArea(area);
|
||||
Put();
|
||||
free(area);
|
||||
}
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VMUserAddressSpace::UnreserveAllAddressRanges()
|
||||
{
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
if (area->id == RESERVED_AREA_ID) {
|
||||
RemoveArea(area);
|
||||
Put();
|
||||
free(area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VMUserAddressSpace::Dump() const
|
||||
{
|
||||
VMAddressSpace::Dump();
|
||||
kprintf("area_hint: %p\n", fAreaHint);
|
||||
|
||||
kprintf("area_list:\n");
|
||||
|
||||
for (VMAddressSpaceAreaList::ConstIterator it = fAreas.GetIterator();
|
||||
VMArea* area = it.Next();) {
|
||||
kprintf(" area 0x%lx: ", area->id);
|
||||
kprintf("base_addr = 0x%lx ", area->Base());
|
||||
kprintf("size = 0x%lx ", area->Size());
|
||||
kprintf("name = '%s' ", area->name);
|
||||
kprintf("protection = 0x%lx\n", area->protection);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! Finds a reserved area that covers the region spanned by \a start and
|
||||
\a size, inserts the \a area into that region and makes sure that
|
||||
there are reserved regions for the remaining parts.
|
||||
*/
|
||||
status_t
|
||||
VMUserAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
|
||||
VMArea* area)
|
||||
{
|
||||
VMArea* next;
|
||||
|
||||
for (VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
(next = it.Next()) != NULL;) {
|
||||
if (next->Base() <= start
|
||||
&& next->Base() + (next->Size() - 1) >= start + (size - 1)) {
|
||||
// This area covers the requested range
|
||||
if (next->id != RESERVED_AREA_ID) {
|
||||
// but it's not reserved space, it's a real area
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (next == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
// Now we have to transfer the requested part of the reserved
|
||||
// range to the new area - and remove, resize or split the old
|
||||
// reserved area.
|
||||
|
||||
if (start == next->Base()) {
|
||||
// the area starts at the beginning of the reserved range
|
||||
fAreas.Insert(next, area);
|
||||
|
||||
if (size == next->Size()) {
|
||||
// the new area fully covers the reversed range
|
||||
fAreas.Remove(next);
|
||||
Put();
|
||||
free(next);
|
||||
} else {
|
||||
// resize the reserved range behind the area
|
||||
next->SetBase(next->Base() + size);
|
||||
next->SetSize(next->Size() - size);
|
||||
}
|
||||
} else if (start + size == next->Base() + next->Size()) {
|
||||
// the area is at the end of the reserved range
|
||||
fAreas.Insert(fAreas.GetNext(next), area);
|
||||
|
||||
// resize the reserved range before the area
|
||||
next->SetSize(start - next->Base());
|
||||
} else {
|
||||
// the area splits the reserved range into two separate ones
|
||||
// we need a new reserved area to cover this space
|
||||
VMArea* reserved = VMArea::CreateReserved(this, next->protection);
|
||||
if (reserved == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
Get();
|
||||
fAreas.Insert(fAreas.GetNext(next), reserved);
|
||||
fAreas.Insert(reserved, area);
|
||||
|
||||
// resize regions
|
||||
reserved->SetSize(next->Base() + next->Size() - start - size);
|
||||
next->SetSize(start - next->Base());
|
||||
reserved->SetBase(start + size);
|
||||
reserved->cache_offset = next->cache_offset;
|
||||
}
|
||||
|
||||
area->SetBase(start);
|
||||
area->SetSize(size);
|
||||
IncrementChangeCount();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! Must be called with this address space's write lock held */
|
||||
status_t
|
||||
VMUserAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end,
|
||||
uint32 addressSpec, VMArea* area)
|
||||
{
|
||||
VMArea* last = NULL;
|
||||
VMArea* next;
|
||||
bool foundSpot = false;
|
||||
|
||||
TRACE(("VMUserAddressSpace::_InsertAreaSlot: address space %p, start "
|
||||
"0x%lx, size %ld, end 0x%lx, addressSpec %ld, area %p\n", this, start,
|
||||
size, end, addressSpec, area));
|
||||
|
||||
// do some sanity checking
|
||||
if (start < fBase || size == 0 || end > fEndAddress
|
||||
|| start + (size - 1) > end)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (addressSpec == B_EXACT_ADDRESS && area->id != RESERVED_AREA_ID) {
|
||||
// search for a reserved area
|
||||
status_t status = _InsertAreaIntoReservedRegion(start, size, area);
|
||||
if (status == B_OK || status == B_BAD_VALUE)
|
||||
return status;
|
||||
|
||||
// There was no reserved area, and the slot doesn't seem to be used
|
||||
// already
|
||||
// TODO: this could be further optimized.
|
||||
}
|
||||
|
||||
size_t alignment = B_PAGE_SIZE;
|
||||
if (addressSpec == B_ANY_KERNEL_BLOCK_ADDRESS) {
|
||||
// align the memory to the next power of two of the size
|
||||
while (alignment < size)
|
||||
alignment <<= 1;
|
||||
}
|
||||
|
||||
start = ROUNDUP(start, alignment);
|
||||
|
||||
// walk up to the spot where we should start searching
|
||||
second_chance:
|
||||
VMAddressSpaceAreaList::Iterator it = fAreas.GetIterator();
|
||||
while ((next = it.Next()) != NULL) {
|
||||
if (next->Base() > start + (size - 1)) {
|
||||
// we have a winner
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
}
|
||||
|
||||
// find the right spot depending on the address specification - the area
|
||||
// will be inserted directly after "last" ("next" is not referenced anymore)
|
||||
|
||||
switch (addressSpec) {
|
||||
case B_ANY_ADDRESS:
|
||||
case B_ANY_KERNEL_ADDRESS:
|
||||
case B_ANY_KERNEL_BLOCK_ADDRESS:
|
||||
{
|
||||
// find a hole big enough for a new area
|
||||
if (last == NULL) {
|
||||
// see if we can build it at the beginning of the virtual map
|
||||
addr_t alignedBase = ROUNDUP(fBase, alignment);
|
||||
if (is_valid_spot(fBase, alignedBase, size,
|
||||
next == NULL ? end : next->Base())) {
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
// keep walking
|
||||
while (next != NULL) {
|
||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||
alignment);
|
||||
if (is_valid_spot(last->Base() + (last->Size() - 1),
|
||||
alignedBase, size, next->Base())) {
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
if (foundSpot)
|
||||
break;
|
||||
|
||||
addr_t alignedBase = ROUNDUP(last->Base() + last->Size(),
|
||||
alignment);
|
||||
if (is_valid_spot(last->Base() + (last->Size() - 1), alignedBase,
|
||||
size, end)) {
|
||||
// got a spot
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
} else if (area->id != RESERVED_AREA_ID) {
|
||||
// We didn't find a free spot - if there are any reserved areas,
|
||||
// we can now test those for free space
|
||||
// TODO: it would make sense to start with the biggest of them
|
||||
it.Rewind();
|
||||
next = it.Next();
|
||||
for (last = NULL; next != NULL; next = it.Next()) {
|
||||
if (next->id != RESERVED_AREA_ID) {
|
||||
last = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: take free space after the reserved area into
|
||||
// account!
|
||||
addr_t alignedBase = ROUNDUP(next->Base(), alignment);
|
||||
if (next->Base() == alignedBase && next->Size() == size) {
|
||||
// The reserved area is entirely covered, and thus,
|
||||
// removed
|
||||
fAreas.Remove(next);
|
||||
|
||||
foundSpot = true;
|
||||
area->SetBase(alignedBase);
|
||||
free(next);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((next->protection & RESERVED_AVOID_BASE) == 0
|
||||
&& alignedBase == next->Base()
|
||||
&& next->Size() >= size) {
|
||||
// The new area will be placed at the beginning of the
|
||||
// reserved area and the reserved area will be offset
|
||||
// and resized
|
||||
foundSpot = true;
|
||||
next->SetBase(next->Base() + size);
|
||||
next->SetSize(next->Size() - size);
|
||||
area->SetBase(alignedBase);
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_valid_spot(next->Base(), alignedBase, size,
|
||||
next->Base() + (next->Size() - 1))) {
|
||||
// The new area will be placed at the end of the
|
||||
// reserved area, and the reserved area will be resized
|
||||
// to make space
|
||||
alignedBase = ROUNDDOWN(
|
||||
next->Base() + next->Size() - size, alignment);
|
||||
|
||||
foundSpot = true;
|
||||
next->SetSize(alignedBase - next->Base());
|
||||
area->SetBase(alignedBase);
|
||||
last = next;
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case B_BASE_ADDRESS:
|
||||
{
|
||||
// find a hole big enough for a new area beginning with "start"
|
||||
if (last == NULL) {
|
||||
// see if we can build it at the beginning of the specified
|
||||
// start
|
||||
if (next == NULL || next->Base() > start + (size - 1)) {
|
||||
foundSpot = true;
|
||||
area->SetBase(start);
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
// keep walking
|
||||
while (next != NULL) {
|
||||
if (next->Base() - (last->Base() + last->Size()) >= size) {
|
||||
// we found a spot (it'll be filled up below)
|
||||
break;
|
||||
}
|
||||
|
||||
last = next;
|
||||
next = it.Next();
|
||||
}
|
||||
|
||||
addr_t lastEnd = last->Base() + (last->Size() - 1);
|
||||
if (next != NULL || end - lastEnd >= size) {
|
||||
// got a spot
|
||||
foundSpot = true;
|
||||
if (lastEnd < start)
|
||||
area->SetBase(start);
|
||||
else
|
||||
area->SetBase(lastEnd + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
// we didn't find a free spot in the requested range, so we'll
|
||||
// try again without any restrictions
|
||||
start = fBase;
|
||||
addressSpec = B_ANY_ADDRESS;
|
||||
last = NULL;
|
||||
goto second_chance;
|
||||
}
|
||||
|
||||
case B_EXACT_ADDRESS:
|
||||
// see if we can create it exactly here
|
||||
if ((last == NULL || last->Base() + (last->Size() - 1) < start)
|
||||
&& (next == NULL || next->Base() > start + (size - 1))) {
|
||||
foundSpot = true;
|
||||
area->SetBase(start);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (!foundSpot)
|
||||
return addressSpec == B_EXACT_ADDRESS ? B_BAD_VALUE : B_NO_MEMORY;
|
||||
|
||||
area->SetSize(size);
|
||||
if (last)
|
||||
fAreas.Insert(fAreas.GetNext(last), area);
|
||||
else
|
||||
fAreas.Insert(fAreas.Head(), area);
|
||||
|
||||
IncrementChangeCount();
|
||||
return B_OK;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||
* Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
#ifndef VM_USER_ADDRESS_SPACE_H
|
||||
#define VM_USER_ADDRESS_SPACE_H
|
||||
|
||||
|
||||
#include <vm/VMAddressSpace.h>
|
||||
|
||||
|
||||
struct VMUserAddressSpace : VMAddressSpace {
|
||||
public:
|
||||
VMUserAddressSpace(team_id id, addr_t base,
|
||||
size_t size);
|
||||
virtual ~VMUserAddressSpace();
|
||||
|
||||
virtual VMArea* FirstArea() const;
|
||||
virtual VMArea* NextArea(VMArea* area) const;
|
||||
|
||||
virtual VMArea* LookupArea(addr_t address) const;
|
||||
virtual status_t InsertArea(void** _address, uint32 addressSpec,
|
||||
addr_t size, VMArea* area);
|
||||
virtual void RemoveArea(VMArea* area);
|
||||
|
||||
virtual bool CanResizeArea(VMArea* area, size_t newSize);
|
||||
virtual status_t ResizeArea(VMArea* area, size_t newSize);
|
||||
virtual status_t ResizeAreaHead(VMArea* area, size_t size);
|
||||
virtual status_t ResizeAreaTail(VMArea* area, size_t size);
|
||||
|
||||
virtual status_t ReserveAddressRange(void** _address,
|
||||
uint32 addressSpec, size_t size,
|
||||
uint32 flags);
|
||||
virtual status_t UnreserveAddressRange(addr_t address,
|
||||
size_t size);
|
||||
virtual void UnreserveAllAddressRanges();
|
||||
|
||||
virtual void Dump() const;
|
||||
|
||||
private:
|
||||
status_t _InsertAreaIntoReservedRegion(addr_t start,
|
||||
size_t size, VMArea* area);
|
||||
status_t _InsertAreaSlot(addr_t start, addr_t size,
|
||||
addr_t end, uint32 addressSpec,
|
||||
VMArea* area);
|
||||
|
||||
private:
|
||||
VMAddressSpaceAreaList fAreas;
|
||||
mutable VMArea* fAreaHint;
|
||||
};
|
||||
|
||||
|
||||
#endif /* VM_USER_ADDRESS_SPACE_H */
|
||||
Reference in New Issue
Block a user