diff --git a/headers/private/kernel/vm/VMAddressSpace.h b/headers/private/kernel/vm/VMAddressSpace.h index e541f099ec..a68835303c 100644 --- a/headers/private/kernel/vm/VMAddressSpace.h +++ b/headers/private/kernel/vm/VMAddressSpace.h @@ -12,13 +12,14 @@ #include +#include #include #include struct VMAddressSpace { public: - class Iterator; + class AreaIterator; public: VMAddressSpace(team_id id, addr_t base, @@ -30,7 +31,7 @@ public: team_id ID() const { return fID; } addr_t Base() const { return fBase; } - size_t Size() const { return fSize; } + addr_t EndAddress() const { return fEndAddress; } size_t FreeSpace() const { return fFreeSpace; } bool IsBeingDeleted() const { return fDeleting; } @@ -57,19 +58,26 @@ public: void IncrementChangeCount() { fChangeCount++; } - VMArea* FirstArea() const - { return fAreas.Head(); } - VMArea* NextArea(VMArea* area) const - { return fAreas.GetNext(area); } + 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); - inline Iterator GetIterator(); + 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(); static status_t Create(team_id teamID, addr_t base, size_t size, bool kernel, @@ -108,7 +116,7 @@ private: private: VMAddressSpace* fHashTableLink; addr_t fBase; - size_t fSize; + addr_t fEndAddress; // base + (size - 1) size_t fFreeSpace; rw_lock fLock; team_id fID; @@ -123,42 +131,65 @@ private: }; -class VMAddressSpace::Iterator { +class VMAddressSpace::AreaIterator { public: - Iterator() + AreaIterator() { } - Iterator(VMAddressSpace* addressSpace) + AreaIterator(VMAddressSpace* addressSpace) : fIterator(addressSpace->fAreas.GetIterator()) { + _SkipReserved(); } bool HasNext() const { - return fIterator.HasNext(); + return fNext != NULL; } VMArea* Next() { - return fIterator.Next(); + VMArea* result = fNext; + _SkipReserved(); + return result; } void Rewind() { fIterator.Rewind(); + _SkipReserved(); } private: - VMAddressSpaceAreaList::Iterator fIterator; + void _SkipReserved() + { + while ((fNext = fIterator.Next()) != NULL + && fNext->id == RESERVED_AREA_ID) { + } + } + +private: + VMAddressSpaceAreaList::Iterator fIterator; + VMArea* fNext; }; -inline VMAddressSpace::Iterator -VMAddressSpace::GetIterator() +inline VMArea* +VMAddressSpace::FirstArea() const { - return Iterator(this); + VMArea* area = fAreas.Head(); + while (area != NULL && area->id == RESERVED_AREA_ID) + area = fAreas.GetNext(area); + return area; +} + + +inline VMAddressSpace::AreaIterator +VMAddressSpace::GetAreaIterator() +{ + return AreaIterator(this); } diff --git a/src/system/kernel/elf.cpp b/src/system/kernel/elf.cpp index bb987f6c14..05076c909f 100644 --- a/src/system/kernel/elf.cpp +++ b/src/system/kernel/elf.cpp @@ -1304,7 +1304,8 @@ public: { // find the runtime loader debug area VMArea* area; - for (VMAddressSpace::Iterator it = team->address_space->GetIterator(); + for (VMAddressSpace::AreaIterator it + = team->address_space->GetAreaIterator(); (area = it.Next()) != NULL;) { if (strcmp(area->name, RUNTIME_LOADER_DEBUG_AREA_NAME) == 0) break; diff --git a/src/system/kernel/vm/VMAddressSpace.cpp b/src/system/kernel/vm/VMAddressSpace.cpp index 42b6b382d0..b93750f082 100644 --- a/src/system/kernel/vm/VMAddressSpace.cpp +++ b/src/system/kernel/vm/VMAddressSpace.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include @@ -88,7 +87,7 @@ VMAddressSpace::VMAddressSpace(team_id id, addr_t base, size_t size, bool kernel) : fBase(base), - fSize(size), + fEndAddress(base + (size - 1)), fFreeSpace(size), fID(id), fRefCount(1), @@ -317,7 +316,7 @@ VMAddressSpace::InsertArea(void** _address, uint32 addressSpec, addr_t size, case B_BASE_ADDRESS: searchBase = (addr_t)*_address; - searchEnd = fBase + (fSize - 1); + searchEnd = fEndAddress; break; case B_ANY_ADDRESS: @@ -328,7 +327,7 @@ VMAddressSpace::InsertArea(void** _address, uint32 addressSpec, addr_t size, // completely (currently needs a userland address space!) if (searchBase == USER_BASE) searchBase = USER_BASE_ANY; - searchEnd = fBase + (fSize - 1); + searchEnd = fEndAddress; break; default: @@ -361,6 +360,64 @@ VMAddressSpace::RemoveArea(VMArea* area) } +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) { @@ -388,6 +445,78 @@ VMAddressSpace::ResizeAreaTail(VMArea* area, size_t size) } +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 { @@ -397,7 +526,7 @@ VMAddressSpace::Dump() const kprintf("fault_count: %ld\n", fFaultCount); kprintf("translation_map: %p\n", &fTranslationMap); kprintf("base: 0x%lx\n", fBase); - kprintf("size: 0x%lx\n", fSize); + kprintf("end: 0x%lx\n", fEndAddress); kprintf("change_count: 0x%lx\n", fChangeCount); kprintf("area_hint: %p\n", fAreaHint); @@ -505,7 +634,7 @@ VMAddressSpace::_InsertAreaSlot(addr_t start, addr_t size, addr_t end, size, end, addressSpec, area)); // do some sanity checking - if (start < fBase || size == 0 || end > fBase + fSize - 1 + if (start < fBase || size == 0 || end > fEndAddress || start + (size - 1) > end) return B_BAD_ADDRESS; @@ -753,7 +882,7 @@ VMAddressSpace::_DumpCommand(int argc, char** argv) /*static*/ int VMAddressSpace::_DumpListCommand(int argc, char** argv) { - kprintf(" address id base size area count " + kprintf(" address id base end area count " " area size\n"); AddressSpaceTable::Iterator it = sAddressSpaceTable.GetIterator(); @@ -769,7 +898,7 @@ VMAddressSpace::_DumpListCommand(int argc, char** argv) } } kprintf("%p %6ld %#010lx %#10lx %10ld %10lld\n", - space, space->ID(), space->Base(), space->Size(), areaCount, + space, space->ID(), space->Base(), space->EndAddress(), areaCount, areaSize); } diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index a47e571e14..b9efce05c4 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -460,30 +460,26 @@ unmap_address_range(VMAddressSpace* addressSpace, addr_t address, addr_t size, // Check, whether the caller is allowed to modify the concerned areas. if (!kernel) { - for (VMAddressSpace::Iterator it = addressSpace->GetIterator(); + for (VMAddressSpace::AreaIterator it = addressSpace->GetAreaIterator(); VMArea* area = it.Next();) { - if (area->id != RESERVED_AREA_ID) { - addr_t areaLast = area->Base() + (area->Size() - 1); - if (area->Base() < lastAddress && address < areaLast) { - if ((area->protection & B_KERNEL_AREA) != 0) - return B_NOT_ALLOWED; - } + addr_t areaLast = area->Base() + (area->Size() - 1); + if (area->Base() < lastAddress && address < areaLast) { + if ((area->protection & B_KERNEL_AREA) != 0) + return B_NOT_ALLOWED; } } } - for (VMAddressSpace::Iterator it = addressSpace->GetIterator(); + for (VMAddressSpace::AreaIterator it = addressSpace->GetAreaIterator(); VMArea* area = it.Next();) { - if (area->id != RESERVED_AREA_ID) { - addr_t areaLast = area->Base() + (area->Size() - 1); - if (area->Base() < lastAddress && address < areaLast) { - status_t error = cut_area(addressSpace, area, address, - lastAddress, NULL, kernel); - if (error != B_OK) - return error; - // Failing after already messing with areas is ugly, but we - // can't do anything about it. - } + addr_t areaLast = area->Base() + (area->Size() - 1); + if (area->Base() < lastAddress && address < areaLast) { + status_t error = cut_area(addressSpace, area, address, + lastAddress, NULL, kernel); + if (error != B_OK) + return error; + // Failing after already messing with areas is ugly, but we + // can't do anything about it. } } @@ -651,29 +647,7 @@ vm_unreserve_address_range(team_id team, void* address, addr_t size) if (!locker.IsLocked()) return B_BAD_TEAM_ID; - // check to see if this address space has entered DELETE state - if (locker.AddressSpace()->IsBeingDeleted()) { - // 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 = (addr_t)address + (size - 1); - for (VMAddressSpace::Iterator it = locker.AddressSpace()->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 - locker.AddressSpace()->RemoveArea(area); - locker.AddressSpace()->Put(); - free(area); - } - } - - return B_OK; + return locker.AddressSpace()->UnreserveAddressRange((addr_t)address, size); } @@ -688,31 +662,8 @@ vm_reserve_address_range(team_id team, void** _address, uint32 addressSpec, if (!locker.IsLocked()) return B_BAD_TEAM_ID; - // check to see if this address space has entered DELETE state - if (locker.AddressSpace()->IsBeingDeleted()) { - // 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(locker.AddressSpace(), flags); - if (area == NULL) - return B_NO_MEMORY; - - status_t status = locker.AddressSpace()->InsertArea(_address, addressSpec, - size, area); - if (status != B_OK) { - free(area); - return status; - } - - // the area is now reserved! - - area->cache_offset = area->Base(); - // we cache the original base address here - - locker.AddressSpace()->Get(); - return B_OK; + return locker.AddressSpace()->ReserveAddressRange(_address, addressSpec, + size, flags); } @@ -2907,16 +2858,7 @@ vm_delete_areas(struct VMAddressSpace* addressSpace) addressSpace->WriteLock(); // remove all reserved areas in this address space - - for (VMAddressSpace::Iterator it = addressSpace->GetIterator(); - VMArea* area = it.Next();) { - if (area->id == RESERVED_AREA_ID) { - // just remove it - addressSpace->RemoveArea(area); - addressSpace->Put(); - free(area); - } - } + addressSpace->UnreserveAllAddressRanges(); // delete all the areas in this address space while (VMArea* area = addressSpace->FirstArea()) @@ -2995,12 +2937,13 @@ vm_free_unused_boot_loader_range(addr_t start, addr_t size) map->ops->lock(map); - for (VMAddressSpace::Iterator it = VMAddressSpace::Kernel()->GetIterator(); + for (VMAddressSpace::AreaIterator it + = VMAddressSpace::Kernel()->GetAreaIterator(); VMArea* area = it.Next();) { addr_t areaStart = area->Base(); addr_t areaEnd = areaStart + (area->Size() - 1); - if (area->id == RESERVED_AREA_ID || areaEnd < start) + if (areaEnd < start) continue; if (areaStart > end) { @@ -4251,23 +4194,10 @@ vm_resize_area(area_id areaID, size_t newSize, bool kernel) if (oldSize < newSize) { // We need to check if all areas of this cache can be resized - for (VMArea* current = cache->areas; current != NULL; current = current->cache_next) { - VMArea* next = current->address_space->NextArea(current); - if (next != NULL && next->Base() <= (current->Base() + newSize)) { - // 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 <= current->Base() - && next->Base() - 1 + next->Size() - >= current->Base() - 1 + newSize) - continue; - + if (!current->address_space->CanResizeArea(current, newSize)) return B_ERROR; - } } } @@ -4282,35 +4212,12 @@ vm_resize_area(area_id areaID, size_t newSize, bool kernel) for (VMArea* current = cache->areas; current != NULL; current = current->cache_next) { - VMArea* next = current->address_space->NextArea(current); - if (next != NULL && next->Base() <= (current->Base() + newSize)) { - if (next->id == RESERVED_AREA_ID - && next->cache_offset <= current->Base() - && next->Base() - 1 + next->Size() - >= current->Base() - 1 + newSize) { - // resize reserved area - addr_t offset = current->Base() + newSize - next->Base(); - if (next->Size() <= offset) { - next->address_space->RemoveArea(next); - free(next); - } else { - next->address_space->ResizeAreaHead(next, - next->Size() - offset); - } - } else { - panic("resize situation for area %p has changed although we " - "should have the address space lock", current); - status = B_ERROR; - break; - } - } - - status = current->address_space->ResizeAreaTail(current, newSize); + status = current->address_space->ResizeArea(current, newSize); if (status != B_OK) break; // We also need to unmap all pages beyond the new size, if the area has - // shrinked + // shrunk if (newSize < oldSize) { vm_unmap_pages(current, current->Base() + newSize, oldSize - newSize, false); @@ -4326,7 +4233,7 @@ vm_resize_area(area_id areaID, size_t newSize, bool kernel) // This can fail, too, in which case we're seriously screwed. for (VMArea* current = cache->areas; current != NULL; current = current->cache_next) { - if (current->address_space->ResizeAreaTail(current, oldSize) + if (current->address_space->ResizeArea(current, oldSize) != B_OK) { panic("vm_resize_area(): Failed and not being able to restore " "original state."); @@ -4792,11 +4699,9 @@ _get_next_area_info(team_id team, int32* cookie, area_info* info, size_t size) return B_BAD_TEAM_ID; VMArea* area; - for (VMAddressSpace::Iterator it = locker.AddressSpace()->GetIterator(); + for (VMAddressSpace::AreaIterator it + = locker.AddressSpace()->GetAreaIterator(); (area = it.Next()) != NULL;) { - if (area->id == RESERVED_AREA_ID) - continue; - if (area->Base() > nextBase) break; }