* Renamed VMAddressSpace::ResizeArea{Head,Tail}() to ShrinkArea{Head,Tail}()

to clarify that they never enlarge the area.
* Reimplemented VMKernelAddressSpace. It is somewhat inspired by Bonwick's
  vmem resource allocator (though we have different requirements):
  - We consider the complete address space to be divided into contiguous
    ranges of type free, reserved, or area, each range being represented by
    a VMKernelAddressRange object.
  - The range objects are managed in an AVL tree and a doubly linked list
    (the latter only for faster iteration) sorted by address. This provides
    O(log(n)) lookup, insertion and removal.
  - For each power of two size we maintain a list of free ranges of at least
    that size. Thus for the most common case of B_ANY*_ADDRESS area
    allocation, we find a free range in constant time (the rest of the
    processing being O(log(n))) with a rather good fit. This should also
    help avoiding address space fragmentation.
  While the new implementation should be faster, particularly with an
  increasing number of areas, I couldn't measure any difference in the -j2
  haiku build. From a cursory test the -j8 build hasn't tangibly benefitted
  either.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34528 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-12-06 17:18:04 +00:00
parent fe30b74fde
commit 40cd019ea0
11 changed files with 874 additions and 530 deletions
+7 -3
View File
@@ -62,6 +62,8 @@ public:
VMAddressSpace*& HashTableLink() { return fHashTableLink; } VMAddressSpace*& HashTableLink() { return fHashTableLink; }
virtual status_t InitObject();
virtual VMArea* FirstArea() const = 0; virtual VMArea* FirstArea() const = 0;
virtual VMArea* NextArea(VMArea* area) const = 0; virtual VMArea* NextArea(VMArea* area) const = 0;
@@ -70,13 +72,15 @@ public:
uint32 protection) = 0; uint32 protection) = 0;
virtual void DeleteArea(VMArea* area) = 0; virtual void DeleteArea(VMArea* area) = 0;
virtual status_t InsertArea(void** _address, uint32 addressSpec, virtual status_t InsertArea(void** _address, uint32 addressSpec,
addr_t size, VMArea* area) = 0; size_t size, VMArea* area) = 0;
virtual void RemoveArea(VMArea* area) = 0; virtual void RemoveArea(VMArea* area) = 0;
virtual bool CanResizeArea(VMArea* area, size_t newSize) = 0; virtual bool CanResizeArea(VMArea* area, size_t newSize) = 0;
virtual status_t ResizeArea(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 ShrinkAreaHead(VMArea* area, size_t newSize)
virtual status_t ResizeAreaTail(VMArea* area, size_t size) = 0; = 0;
virtual status_t ShrinkAreaTail(VMArea* area, size_t newSize)
= 0;
virtual status_t ReserveAddressRange(void** _address, virtual status_t ReserveAddressRange(void** _address,
uint32 addressSpec, size_t size, uint32 addressSpec, size_t size,
+25 -10
View File
@@ -12,6 +12,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <new>
#include <KernelExport.h> #include <KernelExport.h>
#include <util/OpenHashTable.h> #include <util/OpenHashTable.h>
@@ -181,17 +183,24 @@ VMAddressSpace::RemoveAndPut()
} }
status_t
VMAddressSpace::InitObject()
{
return B_OK;
}
void void
VMAddressSpace::Dump() const VMAddressSpace::Dump() const
{ {
kprintf("dump of address space at %p:\n", this); kprintf("dump of address space at %p:\n", this);
kprintf("id: 0x%lx\n", fID); kprintf("id: %" B_PRId32 "\n", fID);
kprintf("ref_count: %ld\n", fRefCount); kprintf("ref_count: %" B_PRId32 "\n", fRefCount);
kprintf("fault_count: %ld\n", fFaultCount); kprintf("fault_count: %" B_PRId32 "\n", fFaultCount);
kprintf("translation_map: %p\n", &fTranslationMap); kprintf("translation_map: %p\n", &fTranslationMap);
kprintf("base: 0x%lx\n", fBase); kprintf("base: %#" B_PRIxADDR "\n", fBase);
kprintf("end: 0x%lx\n", fEndAddress); kprintf("end: %#" B_PRIxADDR "\n", fEndAddress);
kprintf("change_count: 0x%lx\n", fChangeCount); kprintf("change_count: %" B_PRId32 "\n", fChangeCount);
} }
@@ -205,11 +214,17 @@ VMAddressSpace::Create(team_id teamID, addr_t base, size_t size, bool kernel,
if (addressSpace == NULL) if (addressSpace == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
status_t status = addressSpace->InitObject();
if (status != B_OK) {
delete addressSpace;
return status;
}
TRACE(("vm_create_aspace: team %ld (%skernel): %#lx bytes starting at " TRACE(("vm_create_aspace: team %ld (%skernel): %#lx bytes starting at "
"%#lx => %p\n", id, kernel ? "!" : "", size, base, addressSpace)); "%#lx => %p\n", id, kernel ? "!" : "", size, base, addressSpace));
// initialize the corresponding translation map // initialize the corresponding translation map
status_t status = arch_vm_translation_map_init_map( status = arch_vm_translation_map_init_map(
&addressSpace->fTranslationMap, kernel); &addressSpace->fTranslationMap, kernel);
if (status != B_OK) { if (status != B_OK) {
delete addressSpace; delete addressSpace;
@@ -321,9 +336,9 @@ VMAddressSpace::_DumpListCommand(int argc, char** argv)
areaSize += area->Size(); areaSize += area->Size();
} }
} }
kprintf("%p %6ld %#010lx %#10lx %10ld %10lld\n", kprintf("%p %6" B_PRId32 " %#010" B_PRIxADDR " %#10" B_PRIxADDR
space, space->ID(), space->Base(), space->EndAddress(), areaCount, " %10" B_PRId32 " %10" B_PRIdOFF "\n", space, space->ID(),
areaSize); space->Base(), space->EndAddress(), areaCount, areaSize);
} }
return 0; return 0;
+2
View File
@@ -10,6 +10,8 @@
#include <vm/VMArea.h> #include <vm/VMArea.h>
#include <new>
#include <heap.h> #include <heap.h>
File diff suppressed because it is too large Load Diff
+34 -14
View File
@@ -1,10 +1,6 @@
/* /*
* Copyright 2009, Ingo Weinhold, [email protected]. * 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. * 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 #ifndef VM_KERNEL_ADDRESS_SPACE_H
#define VM_KERNEL_ADDRESS_SPACE_H #define VM_KERNEL_ADDRESS_SPACE_H
@@ -21,6 +17,8 @@ public:
size_t size); size_t size);
virtual ~VMKernelAddressSpace(); virtual ~VMKernelAddressSpace();
virtual status_t InitObject();
virtual VMArea* FirstArea() const; virtual VMArea* FirstArea() const;
virtual VMArea* NextArea(VMArea* area) const; virtual VMArea* NextArea(VMArea* area) const;
@@ -29,13 +27,13 @@ public:
uint32 protection); uint32 protection);
virtual void DeleteArea(VMArea* area); virtual void DeleteArea(VMArea* area);
virtual status_t InsertArea(void** _address, uint32 addressSpec, virtual status_t InsertArea(void** _address, uint32 addressSpec,
addr_t size, VMArea* area); size_t size, VMArea* area);
virtual void RemoveArea(VMArea* area); virtual void RemoveArea(VMArea* area);
virtual bool CanResizeArea(VMArea* area, size_t newSize); virtual bool CanResizeArea(VMArea* area, size_t newSize);
virtual status_t ResizeArea(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 ShrinkAreaHead(VMArea* area, size_t newSize);
virtual status_t ResizeAreaTail(VMArea* area, size_t size); virtual status_t ShrinkAreaTail(VMArea* area, size_t newSize);
virtual status_t ReserveAddressRange(void** _address, virtual status_t ReserveAddressRange(void** _address,
uint32 addressSpec, size_t size, uint32 addressSpec, size_t size,
@@ -47,15 +45,37 @@ public:
virtual void Dump() const; virtual void Dump() const;
private: private:
status_t _InsertAreaIntoReservedRegion(addr_t start, typedef VMKernelAddressRange Range;
size_t size, VMKernelArea* area); typedef VMKernelAddressRangeTree RangeTree;
status_t _InsertAreaSlot(addr_t start, addr_t size, typedef DoublyLinkedList<Range,
addr_t end, uint32 addressSpec, DoublyLinkedListMemberGetLink<Range, &Range::listLink> >
VMKernelArea* area); RangeList;
typedef DoublyLinkedList<Range, VMKernelAddressRangeGetFreeListLink>
RangeFreeList;
private: private:
VMKernelAreaList fAreas; inline void _FreeListInsertRange(Range* range, size_t size);
mutable VMArea* fAreaHint; inline void _FreeListRemoveRange(Range* range, size_t size);
void _InsertRange(Range* range);
void _RemoveRange(Range* range);
status_t _AllocateRange(addr_t address,
uint32 addressSpec, size_t size,
bool allowReservedRange, Range*& _range);
Range* _FindFreeRange(addr_t start, size_t size,
size_t alignment, uint32 addressSpec,
bool allowReservedRange,
addr_t& _foundAddress);
void _FreeRange(Range* range);
void _CheckStructures() const;
private:
RangeTree fRangeTree;
RangeList fRangeList;
RangeFreeList* fFreeLists;
int fFreeListCount;
}; };
-10
View File
@@ -39,13 +39,3 @@ VMKernelArea::Create(VMAddressSpace* addressSpace, const char* name,
return area; return area;
} }
/*static*/ VMKernelArea*
VMKernelArea::CreateReserved(VMAddressSpace* addressSpace, uint32 flags)
{
VMKernelArea* area = new(nogrow) VMKernelArea(addressSpace, 0, 0);
if (area != NULL)
area->id = RESERVED_AREA_ID;
return area;
}
+102 -25
View File
@@ -6,13 +6,109 @@
#define VM_KERNEL_AREA_H #define VM_KERNEL_AREA_H
#include <util/AVLTree.h>
#include <vm/VMArea.h> #include <vm/VMArea.h>
struct VMKernelAddressSpace; struct VMKernelAddressSpace;
struct VMKernelArea;
struct VMKernelArea : VMArea { struct VMKernelAddressRange : AVLTreeNode {
public:
// range types
enum {
RANGE_FREE,
RANGE_RESERVED,
RANGE_AREA
};
public:
DoublyLinkedListLink<VMKernelAddressRange> listLink;
addr_t base;
size_t size;
union {
VMKernelArea* area;
struct {
addr_t base;
uint32 flags;
} reserved;
DoublyLinkedListLink<VMKernelAddressRange> freeListLink;
};
int type;
public:
VMKernelAddressRange(addr_t base, size_t size, int type)
:
base(base),
size(size),
type(type)
{
}
VMKernelAddressRange(addr_t base, size_t size,
const VMKernelAddressRange* other)
:
base(base),
size(size),
type(other->type)
{
if (type == RANGE_RESERVED) {
reserved.base = other->reserved.base;
reserved.flags = other->reserved.flags;
}
}
};
struct VMKernelAddressRangeTreeDefinition {
typedef addr_t Key;
typedef VMKernelAddressRange Value;
AVLTreeNode* GetAVLTreeNode(Value* value) const
{
return value;
}
Value* GetValue(AVLTreeNode* node) const
{
return static_cast<Value*>(node);
}
int Compare(addr_t a, const Value* _b) const
{
addr_t b = _b->base;
if (a == b)
return 0;
return a < b ? -1 : 1;
}
int Compare(const Value* a, const Value* b) const
{
return Compare(a->base, b);
}
};
typedef AVLTree<VMKernelAddressRangeTreeDefinition> VMKernelAddressRangeTree;
struct VMKernelAddressRangeGetFreeListLink {
typedef DoublyLinkedListLink<VMKernelAddressRange> Link;
inline Link* operator()(VMKernelAddressRange* range) const
{
return &range->freeListLink;
}
inline const Link* operator()(const VMKernelAddressRange* range) const
{
return &range->freeListLink;
}
};
struct VMKernelArea : VMArea, AVLTreeNode {
VMKernelArea(VMAddressSpace* addressSpace, VMKernelArea(VMAddressSpace* addressSpace,
uint32 wiring, uint32 protection); uint32 wiring, uint32 protection);
~VMKernelArea(); ~VMKernelArea();
@@ -20,34 +116,15 @@ struct VMKernelArea : VMArea {
static VMKernelArea* Create(VMAddressSpace* addressSpace, static VMKernelArea* Create(VMAddressSpace* addressSpace,
const char* name, uint32 wiring, const char* name, uint32 wiring,
uint32 protection); uint32 protection);
static VMKernelArea* CreateReserved(VMAddressSpace* addressSpace,
uint32 flags);
DoublyLinkedListLink<VMKernelArea>& AddressSpaceLink() VMKernelAddressRange* Range() const
{ return fAddressSpaceLink; } { return fRange; }
const DoublyLinkedListLink<VMKernelArea>& AddressSpaceLink() const void SetRange(VMKernelAddressRange* range)
{ return fAddressSpaceLink; } { fRange = range; }
private: private:
DoublyLinkedListLink<VMKernelArea> fAddressSpaceLink; VMKernelAddressRange* fRange;
}; };
struct VMKernelAreaGetLink {
inline DoublyLinkedListLink<VMKernelArea>* operator()(
VMKernelArea* area) const
{
return &area->AddressSpaceLink();
}
inline const DoublyLinkedListLink<VMKernelArea>* operator()(
const VMKernelArea* area) const
{
return &area->AddressSpaceLink();
}
};
typedef DoublyLinkedList<VMKernelArea, VMKernelAreaGetLink> VMKernelAreaList;
#endif // VM_KERNEL_AREA_H #endif // VM_KERNEL_AREA_H
+13 -14
View File
@@ -41,7 +41,7 @@ is_valid_spot(addr_t base, addr_t alignedBase, addr_t size, addr_t limit)
VMUserAddressSpace::VMUserAddressSpace(team_id id, addr_t base, size_t size) VMUserAddressSpace::VMUserAddressSpace(team_id id, addr_t base, size_t size)
: :
VMAddressSpace(id, base, size, "kernel address space"), VMAddressSpace(id, base, size, "address space"),
fAreaHint(NULL) fAreaHint(NULL)
{ {
} }
@@ -118,7 +118,7 @@ VMUserAddressSpace::LookupArea(addr_t address) const
*/ */
status_t status_t
VMUserAddressSpace::InsertArea(void** _address, uint32 addressSpec, VMUserAddressSpace::InsertArea(void** _address, uint32 addressSpec,
addr_t size, VMArea* _area) size_t size, VMArea* _area)
{ {
VMUserArea* area = static_cast<VMUserArea*>(_area); VMUserArea* area = static_cast<VMUserArea*>(_area);
@@ -225,22 +225,21 @@ VMUserAddressSpace::ResizeArea(VMArea* _area, size_t newSize)
addr_t offset = area->Base() + newSize - next->Base(); addr_t offset = area->Base() + newSize - next->Base();
if (next->Size() <= offset) { if (next->Size() <= offset) {
RemoveArea(next); RemoveArea(next);
free(next); delete next;
} else { } else {
status_t error = ResizeAreaHead(next, next->Size() - offset); status_t error = ShrinkAreaHead(next, next->Size() - offset);
if (error != B_OK) if (error != B_OK)
return error; return error;
} }
} }
return ResizeAreaTail(area, newSize); area->SetSize(newSize);
// TODO: In case of error we should undo the change to the reserved return B_OK;
// area.
} }
status_t status_t
VMUserAddressSpace::ResizeAreaHead(VMArea* area, size_t size) VMUserAddressSpace::ShrinkAreaHead(VMArea* area, size_t size)
{ {
size_t oldSize = area->Size(); size_t oldSize = area->Size();
if (size == oldSize) if (size == oldSize)
@@ -254,7 +253,7 @@ VMUserAddressSpace::ResizeAreaHead(VMArea* area, size_t size)
status_t status_t
VMUserAddressSpace::ResizeAreaTail(VMArea* area, size_t size) VMUserAddressSpace::ShrinkAreaTail(VMArea* area, size_t size)
{ {
size_t oldSize = area->Size(); size_t oldSize = area->Size();
if (size == oldSize) if (size == oldSize)
@@ -283,7 +282,7 @@ VMUserAddressSpace::ReserveAddressRange(void** _address, uint32 addressSpec,
status_t status = InsertArea(_address, addressSpec, size, area); status_t status = InsertArea(_address, addressSpec, size, area);
if (status != B_OK) { if (status != B_OK) {
free(area); delete area;
return status; return status;
} }
@@ -316,7 +315,7 @@ VMUserAddressSpace::UnreserveAddressRange(addr_t address, size_t size)
// remove reserved range // remove reserved range
RemoveArea(area); RemoveArea(area);
Put(); Put();
free(area); delete area;
} }
} }
@@ -332,7 +331,7 @@ VMUserAddressSpace::UnreserveAllAddressRanges()
if (area->id == RESERVED_AREA_ID) { if (area->id == RESERVED_AREA_ID) {
RemoveArea(area); RemoveArea(area);
Put(); Put();
free(area); delete area;
} }
} }
} }
@@ -396,7 +395,7 @@ VMUserAddressSpace::_InsertAreaIntoReservedRegion(addr_t start, size_t size,
// the new area fully covers the reversed range // the new area fully covers the reversed range
fAreas.Remove(next); fAreas.Remove(next);
Put(); Put();
free(next); delete next;
} else { } else {
// resize the reserved range behind the area // resize the reserved range behind the area
next->SetBase(next->Base() + size); next->SetBase(next->Base() + size);
@@ -556,7 +555,7 @@ second_chance:
foundSpot = true; foundSpot = true;
area->SetBase(alignedBase); area->SetBase(alignedBase);
free(next); delete next;
break; break;
} }
+3 -7
View File
@@ -1,10 +1,6 @@
/* /*
* Copyright 2009, Ingo Weinhold, [email protected]. * 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. * 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 #ifndef VM_USER_ADDRESS_SPACE_H
#define VM_USER_ADDRESS_SPACE_H #define VM_USER_ADDRESS_SPACE_H
@@ -29,13 +25,13 @@ public:
uint32 protection); uint32 protection);
virtual void DeleteArea(VMArea* area); virtual void DeleteArea(VMArea* area);
virtual status_t InsertArea(void** _address, uint32 addressSpec, virtual status_t InsertArea(void** _address, uint32 addressSpec,
addr_t size, VMArea* area); size_t size, VMArea* area);
virtual void RemoveArea(VMArea* area); virtual void RemoveArea(VMArea* area);
virtual bool CanResizeArea(VMArea* area, size_t newSize); virtual bool CanResizeArea(VMArea* area, size_t newSize);
virtual status_t ResizeArea(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 ShrinkAreaHead(VMArea* area, size_t newSize);
virtual status_t ResizeAreaTail(VMArea* area, size_t size); virtual status_t ShrinkAreaTail(VMArea* area, size_t newSize);
virtual status_t ReserveAddressRange(void** _address, virtual status_t ReserveAddressRange(void** _address,
uint32 addressSpec, size_t size, uint32 addressSpec, size_t size,
+3 -1
View File
@@ -45,7 +45,9 @@ VMUserArea::Create(VMAddressSpace* addressSpace, const char* name,
VMUserArea::CreateReserved(VMAddressSpace* addressSpace, uint32 flags) VMUserArea::CreateReserved(VMAddressSpace* addressSpace, uint32 flags)
{ {
VMUserArea* area = new(nogrow) VMUserArea(addressSpace, 0, 0); VMUserArea* area = new(nogrow) VMUserArea(addressSpace, 0, 0);
if (area != NULL) if (area != NULL) {
area->id = RESERVED_AREA_ID; area->id = RESERVED_AREA_ID;
area->protection = flags;
}
return area; return area;
} }
+5 -5
View File
@@ -338,7 +338,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
size_t oldSize = area->Size(); size_t oldSize = area->Size();
size_t newSize = address - area->Base(); size_t newSize = address - area->Base();
status_t error = addressSpace->ResizeAreaTail(area, newSize); status_t error = addressSpace->ShrinkAreaTail(area, newSize);
if (error != B_OK) if (error != B_OK)
return error; return error;
@@ -350,7 +350,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
&& list_is_empty(&cache->consumers)) { && list_is_empty(&cache->consumers)) {
error = cache->Resize(cache->virtual_base + newSize); error = cache->Resize(cache->virtual_base + newSize);
if (error != B_OK) { if (error != B_OK) {
addressSpace->ResizeAreaTail(area, oldSize); addressSpace->ShrinkAreaTail(area, oldSize);
return error; return error;
} }
} }
@@ -368,7 +368,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
vm_unmap_pages(area, oldBase, newBase - oldBase, false); vm_unmap_pages(area, oldBase, newBase - oldBase, false);
// resize the area // resize the area
status_t error = addressSpace->ResizeAreaHead(area, newSize); status_t error = addressSpace->ShrinkAreaHead(area, newSize);
if (error != B_OK) if (error != B_OK)
return error; return error;
@@ -392,7 +392,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
// resize the area // resize the area
addr_t oldSize = area->Size(); addr_t oldSize = area->Size();
status_t error = addressSpace->ResizeAreaTail(area, firstNewSize); status_t error = addressSpace->ShrinkAreaTail(area, firstNewSize);
if (error != B_OK) if (error != B_OK)
return error; return error;
@@ -408,7 +408,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
B_EXACT_ADDRESS, area->wiring, area->protection, REGION_NO_PRIVATE_MAP, B_EXACT_ADDRESS, area->wiring, area->protection, REGION_NO_PRIVATE_MAP,
&secondArea, area->name, false, kernel); &secondArea, area->name, false, kernel);
if (error != B_OK) { if (error != B_OK) {
addressSpace->ResizeAreaTail(area, oldSize); addressSpace->ShrinkAreaTail(area, oldSize);
return error; return error;
} }