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:
Ingo Weinhold
2009-12-04 14:45:08 +00:00
parent 38a97b2c36
commit e2518ddbb1
8 changed files with 1471 additions and 666 deletions
+35 -63
View File
@@ -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()
{
+4
View File
@@ -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; }
+2 -1
View File
@@ -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)
;
+26 -602
View File
@@ -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 */
+645
View File
@@ -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;
}
+57
View File
@@ -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 */