From def9898c9b96d366f32c39f0143e265442bd429a Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Wed, 2 Dec 2009 20:16:31 +0000 Subject: [PATCH] Moved the three address space locker classes into a separate pair of header/source files. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34451 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/vm/Jamfile | 1 + .../kernel/vm/VMAddressSpaceLocking.cpp | 549 +++++++++++++++ src/system/kernel/vm/VMAddressSpaceLocking.h | 138 ++++ src/system/kernel/vm/vm.cpp | 638 +----------------- 4 files changed, 689 insertions(+), 637 deletions(-) create mode 100644 src/system/kernel/vm/VMAddressSpaceLocking.cpp create mode 100644 src/system/kernel/vm/VMAddressSpaceLocking.h diff --git a/src/system/kernel/vm/Jamfile b/src/system/kernel/vm/Jamfile index 90ec5c9a44..9d6bb5d3f4 100644 --- a/src/system/kernel/vm/Jamfile +++ b/src/system/kernel/vm/Jamfile @@ -9,6 +9,7 @@ KernelMergeObject kernel_vm.o : vm_daemons.cpp vm_page.cpp VMAddressSpace.cpp + VMAddressSpaceLocking.cpp VMAnonymousCache.cpp VMAnonymousNoSwapCache.cpp VMArea.cpp diff --git a/src/system/kernel/vm/VMAddressSpaceLocking.cpp b/src/system/kernel/vm/VMAddressSpaceLocking.cpp new file mode 100644 index 0000000000..62a1568a1e --- /dev/null +++ b/src/system/kernel/vm/VMAddressSpaceLocking.cpp @@ -0,0 +1,549 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +#include "VMAddressSpaceLocking.h" + +#include + +#include +#include +#include +#include + + +// #pragma mark - AddressSpaceLockerBase + + +/*static*/ VMAddressSpace* +AddressSpaceLockerBase::GetAddressSpaceByAreaID(area_id id) +{ + VMAddressSpace* addressSpace = NULL; + + VMAreaHash::ReadLock(); + + VMArea* area = VMAreaHash::LookupLocked(id); + if (area != NULL) { + addressSpace = area->address_space; + addressSpace->Get(); + } + + VMAreaHash::ReadUnlock(); + + return addressSpace; +} + + +// #pragma mark - AddressSpaceReadLocker + + +AddressSpaceReadLocker::AddressSpaceReadLocker(team_id team) + : + fSpace(NULL), + fLocked(false) +{ + SetTo(team); +} + + +/*! Takes over the reference of the address space, if \a getNewReference is + \c false. +*/ +AddressSpaceReadLocker::AddressSpaceReadLocker(VMAddressSpace* space, + bool getNewReference) + : + fSpace(NULL), + fLocked(false) +{ + SetTo(space, getNewReference); +} + + +AddressSpaceReadLocker::AddressSpaceReadLocker() + : + fSpace(NULL), + fLocked(false) +{ +} + + +AddressSpaceReadLocker::~AddressSpaceReadLocker() +{ + Unset(); +} + + +void +AddressSpaceReadLocker::Unset() +{ + Unlock(); + if (fSpace != NULL) + fSpace->Put(); +} + + +status_t +AddressSpaceReadLocker::SetTo(team_id team) +{ + fSpace = VMAddressSpace::Get(team); + if (fSpace == NULL) + return B_BAD_TEAM_ID; + + fSpace->ReadLock(); + fLocked = true; + return B_OK; +} + + +/*! Takes over the reference of the address space, if \a getNewReference is + \c false. +*/ +void +AddressSpaceReadLocker::SetTo(VMAddressSpace* space, bool getNewReference) +{ + fSpace = space; + + if (getNewReference) + fSpace->Get(); + + fSpace->ReadLock(); + fLocked = true; +} + + +status_t +AddressSpaceReadLocker::SetFromArea(area_id areaID, VMArea*& area) +{ + fSpace = GetAddressSpaceByAreaID(areaID); + if (fSpace == NULL) + return B_BAD_TEAM_ID; + + fSpace->ReadLock(); + + area = VMAreaHash::Lookup(areaID); + + if (area == NULL || area->address_space != fSpace) { + fSpace->ReadUnlock(); + return B_BAD_VALUE; + } + + fLocked = true; + return B_OK; +} + + +bool +AddressSpaceReadLocker::Lock() +{ + if (fLocked) + return true; + if (fSpace == NULL) + return false; + + fSpace->ReadLock(); + fLocked = true; + + return true; +} + + +void +AddressSpaceReadLocker::Unlock() +{ + if (fLocked) { + fSpace->ReadUnlock(); + fLocked = false; + } +} + + +// #pragma mark - AddressSpaceWriteLocker + + +AddressSpaceWriteLocker::AddressSpaceWriteLocker(team_id team) + : + fSpace(NULL), + fLocked(false), + fDegraded(false) +{ + SetTo(team); +} + + +AddressSpaceWriteLocker::AddressSpaceWriteLocker() + : + fSpace(NULL), + fLocked(false), + fDegraded(false) +{ +} + + +AddressSpaceWriteLocker::~AddressSpaceWriteLocker() +{ + Unset(); +} + + +void +AddressSpaceWriteLocker::Unset() +{ + Unlock(); + if (fSpace != NULL) + fSpace->Put(); +} + + +status_t +AddressSpaceWriteLocker::SetTo(team_id team) +{ + fSpace = VMAddressSpace::Get(team); + if (fSpace == NULL) + return B_BAD_TEAM_ID; + + fSpace->WriteLock(); + fLocked = true; + return B_OK; +} + + +status_t +AddressSpaceWriteLocker::SetFromArea(area_id areaID, VMArea*& area) +{ + fSpace = GetAddressSpaceByAreaID(areaID); + if (fSpace == NULL) + return B_BAD_VALUE; + + fSpace->WriteLock(); + + area = VMAreaHash::Lookup(areaID); + + if (area == NULL || area->address_space != fSpace) { + fSpace->WriteUnlock(); + return B_BAD_VALUE; + } + + fLocked = true; + return B_OK; +} + + +status_t +AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID, + bool allowKernel, VMArea*& area) +{ + VMAreaHash::ReadLock(); + + area = VMAreaHash::LookupLocked(areaID); + if (area != NULL + && (area->address_space->ID() == team + || (allowKernel && team == VMAddressSpace::KernelID()))) { + fSpace = area->address_space; + fSpace->Get(); + } + + VMAreaHash::ReadUnlock(); + + if (fSpace == NULL) + return B_BAD_VALUE; + + // Second try to get the area -- this time with the address space + // write lock held + + fSpace->WriteLock(); + + area = VMAreaHash::Lookup(areaID); + + if (area == NULL) { + fSpace->WriteUnlock(); + return B_BAD_VALUE; + } + + fLocked = true; + return B_OK; +} + + +status_t +AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID, + VMArea*& area) +{ + return SetFromArea(team, areaID, false, area); +} + + +void +AddressSpaceWriteLocker::Unlock() +{ + if (fLocked) { + if (fDegraded) + fSpace->ReadUnlock(); + else + fSpace->WriteUnlock(); + fLocked = false; + fDegraded = false; + } +} + + +void +AddressSpaceWriteLocker::DegradeToReadLock() +{ + // TODO: the current R/W lock implementation just keeps the write lock here + fSpace->ReadLock(); + fSpace->WriteUnlock(); + fDegraded = true; +} + + +// #pragma mark - MultiAddressSpaceLocker + + +MultiAddressSpaceLocker::MultiAddressSpaceLocker() + : + fItems(NULL), + fCapacity(0), + fCount(0), + fLocked(false) +{ +} + + +MultiAddressSpaceLocker::~MultiAddressSpaceLocker() +{ + Unset(); + free(fItems); +} + + +/*static*/ int +MultiAddressSpaceLocker::_CompareItems(const void* _a, const void* _b) +{ + lock_item* a = (lock_item*)_a; + lock_item* b = (lock_item*)_b; + return a->space->ID() - b->space->ID(); +} + + +bool +MultiAddressSpaceLocker::_ResizeIfNeeded() +{ + if (fCount == fCapacity) { + lock_item* items = (lock_item*)realloc(fItems, + (fCapacity + 4) * sizeof(lock_item)); + if (items == NULL) + return false; + + fCapacity += 4; + fItems = items; + } + + return true; +} + + +int32 +MultiAddressSpaceLocker::_IndexOfAddressSpace(VMAddressSpace* space) const +{ + for (int32 i = 0; i < fCount; i++) { + if (fItems[i].space == space) + return i; + } + + return -1; +} + + +status_t +MultiAddressSpaceLocker::_AddAddressSpace(VMAddressSpace* space, + bool writeLock, VMAddressSpace** _space) +{ + if (!space) + return B_BAD_VALUE; + + int32 index = _IndexOfAddressSpace(space); + if (index < 0) { + if (!_ResizeIfNeeded()) { + space->Put(); + return B_NO_MEMORY; + } + + lock_item& item = fItems[fCount++]; + item.space = space; + item.write_lock = writeLock; + } else { + + // one reference is enough + space->Put(); + + fItems[index].write_lock |= writeLock; + } + + if (_space != NULL) + *_space = space; + + return B_OK; +} + + +void +MultiAddressSpaceLocker::Unset() +{ + Unlock(); + + for (int32 i = 0; i < fCount; i++) + fItems[i].space->Put(); + + fCount = 0; +} + + +status_t +MultiAddressSpaceLocker::Lock() +{ + ASSERT(!fLocked); + + qsort(fItems, fCount, sizeof(lock_item), &_CompareItems); + + for (int32 i = 0; i < fCount; i++) { + status_t status; + if (fItems[i].write_lock) + status = fItems[i].space->WriteLock(); + else + status = fItems[i].space->ReadLock(); + + if (status < B_OK) { + while (--i >= 0) { + if (fItems[i].write_lock) + fItems[i].space->WriteUnlock(); + else + fItems[i].space->ReadUnlock(); + } + return status; + } + } + + fLocked = true; + return B_OK; +} + + +void +MultiAddressSpaceLocker::Unlock() +{ + if (!fLocked) + return; + + for (int32 i = 0; i < fCount; i++) { + if (fItems[i].write_lock) + fItems[i].space->WriteUnlock(); + else + fItems[i].space->ReadUnlock(); + } + + fLocked = false; +} + + +/*! Adds all address spaces of the areas associated with the given area's cache, + locks them, and locks the cache (including a reference to it). It retries + until the situation is stable (i.e. the neither cache nor cache's areas + changed) or an error occurs. +*/ +status_t +MultiAddressSpaceLocker::AddAreaCacheAndLock(area_id areaID, + bool writeLockThisOne, bool writeLockOthers, VMArea*& _area, + VMCache** _cache) +{ + // remember the original state + int originalCount = fCount; + lock_item* originalItems = NULL; + if (fCount > 0) { + originalItems = new(nothrow) lock_item[fCount]; + if (originalItems == NULL) + return B_NO_MEMORY; + memcpy(originalItems, fItems, fCount * sizeof(lock_item)); + } + ArrayDeleter _(originalItems); + + // get the cache + VMCache* cache; + VMArea* area; + status_t error; + { + AddressSpaceReadLocker locker; + error = locker.SetFromArea(areaID, area); + if (error != B_OK) + return error; + + cache = vm_area_get_locked_cache(area); + } + + while (true) { + // add all areas + VMArea* firstArea = cache->areas; + for (VMArea* current = firstArea; current; + current = current->cache_next) { + error = AddArea(current->id, + current == area ? writeLockThisOne : writeLockOthers); + if (error != B_OK) { + vm_area_put_locked_cache(cache); + return error; + } + } + + // unlock the cache and attempt to lock the address spaces + vm_area_put_locked_cache(cache); + + error = Lock(); + if (error != B_OK) + return error; + + // lock the cache again and check whether anything has changed + + // check whether the area is gone in the meantime + area = VMAreaHash::Lookup(areaID); + + if (area == NULL) { + Unlock(); + return B_BAD_VALUE; + } + + // lock the cache + VMCache* oldCache = cache; + cache = vm_area_get_locked_cache(area); + + // If neither the area's cache has changed nor its area list we're + // done. + if (cache == oldCache && firstArea == cache->areas) { + _area = area; + if (_cache != NULL) + *_cache = cache; + return B_OK; + } + + // Restore the original state and try again. + + // Unlock the address spaces, but keep the cache locked for the next + // iteration. + Unlock(); + + // Get an additional reference to the original address spaces. + for (int32 i = 0; i < originalCount; i++) + originalItems[i].space->Get(); + + // Release all references to the current address spaces. + for (int32 i = 0; i < fCount; i++) + fItems[i].space->Put(); + + // Copy over the original state. + fCount = originalCount; + if (originalItems != NULL) + memcpy(fItems, originalItems, fCount * sizeof(lock_item)); + } +} + + diff --git a/src/system/kernel/vm/VMAddressSpaceLocking.h b/src/system/kernel/vm/VMAddressSpaceLocking.h new file mode 100644 index 0000000000..b0d23900c0 --- /dev/null +++ b/src/system/kernel/vm/VMAddressSpaceLocking.h @@ -0,0 +1,138 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef VM_ADDRESS_SPACE_LOCKING_H +#define VM_ADDRESS_SPACE_LOCKING_H + + +#include + +#include + + +struct VMAddressSpace; +struct VMArea; +struct VMCache; + + +class AddressSpaceLockerBase { +public: + static VMAddressSpace* GetAddressSpaceByAreaID(area_id id); +}; + + +class AddressSpaceReadLocker : private AddressSpaceLockerBase { +public: + AddressSpaceReadLocker(team_id team); + AddressSpaceReadLocker(VMAddressSpace* space, + bool getNewReference); + AddressSpaceReadLocker(); + ~AddressSpaceReadLocker(); + + status_t SetTo(team_id team); + void SetTo(VMAddressSpace* space, + bool getNewReference); + status_t SetFromArea(area_id areaID, VMArea*& area); + + bool IsLocked() const { return fLocked; } + bool Lock(); + void Unlock(); + + void Unset(); + + VMAddressSpace* AddressSpace() const { return fSpace; } + +private: + VMAddressSpace* fSpace; + bool fLocked; +}; + + +class AddressSpaceWriteLocker : private AddressSpaceLockerBase { +public: + AddressSpaceWriteLocker(team_id team); + AddressSpaceWriteLocker(); + ~AddressSpaceWriteLocker(); + + status_t SetTo(team_id team); + status_t SetFromArea(area_id areaID, VMArea*& area); + status_t SetFromArea(team_id team, area_id areaID, + bool allowKernel, VMArea*& area); + status_t SetFromArea(team_id team, area_id areaID, + VMArea*& area); + + bool IsLocked() const { return fLocked; } + void Unlock(); + + void DegradeToReadLock(); + void Unset(); + + VMAddressSpace* AddressSpace() const { return fSpace; } + +private: + VMAddressSpace* fSpace; + bool fLocked; + bool fDegraded; +}; + + +class MultiAddressSpaceLocker : private AddressSpaceLockerBase { +public: + MultiAddressSpaceLocker(); + ~MultiAddressSpaceLocker(); + + inline status_t AddTeam(team_id team, bool writeLock, + VMAddressSpace** _space = NULL); + inline status_t AddArea(area_id area, bool writeLock, + VMAddressSpace** _space = NULL); + + status_t AddAreaCacheAndLock(area_id areaID, + bool writeLockThisOne, bool writeLockOthers, + VMArea*& _area, VMCache** _cache = NULL); + + status_t Lock(); + void Unlock(); + bool IsLocked() const { return fLocked; } + + void Unset(); + +private: + struct lock_item { + VMAddressSpace* space; + bool write_lock; + }; + + bool _ResizeIfNeeded(); + int32 _IndexOfAddressSpace(VMAddressSpace* space) + const; + status_t _AddAddressSpace(VMAddressSpace* space, + bool writeLock, VMAddressSpace** _space); + + static int _CompareItems(const void* _a, const void* _b); + + lock_item* fItems; + int32 fCapacity; + int32 fCount; + bool fLocked; +}; + + +inline status_t +MultiAddressSpaceLocker::AddTeam(team_id team, bool writeLock, + VMAddressSpace** _space) +{ + return _AddAddressSpace(VMAddressSpace::Get(team), writeLock, _space); +} + + +inline status_t +MultiAddressSpaceLocker::AddArea(area_id area, bool writeLock, + VMAddressSpace** _space) +{ + return _AddAddressSpace(GetAddressSpaceByAreaID(area), writeLock, _space); +} + + +#endif // VM_ADDRESS_SPACE_LOCKING_H diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index c065510b11..bf078fb2a7 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -50,6 +50,7 @@ #include #include +#include "VMAddressSpaceLocking.h" #include "VMAnonymousCache.h" #include "IORequest.h" @@ -68,95 +69,6 @@ #endif -class AddressSpaceReadLocker { -public: - AddressSpaceReadLocker(team_id team); - AddressSpaceReadLocker(VMAddressSpace* space, bool getNewReference); - AddressSpaceReadLocker(); - ~AddressSpaceReadLocker(); - - status_t SetTo(team_id team); - void SetTo(VMAddressSpace* space, bool getNewReference); - status_t SetFromArea(area_id areaID, VMArea*& area); - - bool IsLocked() const { return fLocked; } - bool Lock(); - void Unlock(); - - void Unset(); - - VMAddressSpace* AddressSpace() { return fSpace; } - -private: - VMAddressSpace* fSpace; - bool fLocked; -}; - -class AddressSpaceWriteLocker { -public: - AddressSpaceWriteLocker(team_id team); - AddressSpaceWriteLocker(); - ~AddressSpaceWriteLocker(); - - status_t SetTo(team_id team); - status_t SetFromArea(area_id areaID, VMArea*& area); - status_t SetFromArea(team_id team, area_id areaID, bool allowKernel, - VMArea*& area); - status_t SetFromArea(team_id team, area_id areaID, VMArea*& area); - - bool IsLocked() const { return fLocked; } - void Unlock(); - - void DegradeToReadLock(); - void Unset(); - - VMAddressSpace* AddressSpace() { return fSpace; } - -private: - VMAddressSpace* fSpace; - bool fLocked; - bool fDegraded; -}; - -class MultiAddressSpaceLocker { -public: - MultiAddressSpaceLocker(); - ~MultiAddressSpaceLocker(); - - inline status_t AddTeam(team_id team, bool writeLock, - VMAddressSpace** _space = NULL); - inline status_t AddArea(area_id area, bool writeLock, - VMAddressSpace** _space = NULL); - - status_t AddAreaCacheAndLock(area_id areaID, bool writeLockThisOne, - bool writeLockOthers, VMArea*& _area, VMCache** _cache = NULL); - - status_t Lock(); - void Unlock(); - bool IsLocked() const { return fLocked; } - - void Unset(); - -private: - struct lock_item { - VMAddressSpace* space; - bool write_lock; - }; - - bool _ResizeIfNeeded(); - int32 _IndexOfAddressSpace(VMAddressSpace* space) const; - status_t _AddAddressSpace(VMAddressSpace* space, bool writeLock, - VMAddressSpace** _space); - - static int _CompareItems(const void* _a, const void* _b); - - lock_item* fItems; - int32 fCapacity; - int32 fCount; - bool fLocked; -}; - - class AreaCacheLocking { public: inline bool Lock(VMCache* lockable) @@ -216,7 +128,6 @@ static cache_info* sCacheInfoTable; // function declarations static void delete_area(VMAddressSpace* addressSpace, VMArea* area); -static VMAddressSpace* get_address_space_by_area_id(area_id id); static status_t vm_soft_fault(VMAddressSpace* addressSpace, addr_t address, bool isWrite, bool isUser); static status_t map_backing_store(VMAddressSpace* addressSpace, @@ -231,534 +142,6 @@ static size_t sKernelAddressSpaceLeft = KERNEL_SIZE; // #pragma mark - -AddressSpaceReadLocker::AddressSpaceReadLocker(team_id team) - : - fSpace(NULL), - fLocked(false) -{ - SetTo(team); -} - - -/*! Takes over the reference of the address space, if \a getNewReference is - \c false. -*/ -AddressSpaceReadLocker::AddressSpaceReadLocker(VMAddressSpace* space, - bool getNewReference) - : - fSpace(NULL), - fLocked(false) -{ - SetTo(space, getNewReference); -} - - -AddressSpaceReadLocker::AddressSpaceReadLocker() - : - fSpace(NULL), - fLocked(false) -{ -} - - -AddressSpaceReadLocker::~AddressSpaceReadLocker() -{ - Unset(); -} - - -void -AddressSpaceReadLocker::Unset() -{ - Unlock(); - if (fSpace != NULL) - fSpace->Put(); -} - - -status_t -AddressSpaceReadLocker::SetTo(team_id team) -{ - fSpace = VMAddressSpace::Get(team); - if (fSpace == NULL) - return B_BAD_TEAM_ID; - - fSpace->ReadLock(); - fLocked = true; - return B_OK; -} - - -/*! Takes over the reference of the address space, if \a getNewReference is - \c false. -*/ -void -AddressSpaceReadLocker::SetTo(VMAddressSpace* space, bool getNewReference) -{ - fSpace = space; - - if (getNewReference) - fSpace->Get(); - - fSpace->ReadLock(); - fLocked = true; -} - - -status_t -AddressSpaceReadLocker::SetFromArea(area_id areaID, VMArea*& area) -{ - fSpace = get_address_space_by_area_id(areaID); - if (fSpace == NULL) - return B_BAD_TEAM_ID; - - fSpace->ReadLock(); - - area = VMAreaHash::Lookup(areaID); - - if (area == NULL || area->address_space != fSpace) { - fSpace->ReadUnlock(); - return B_BAD_VALUE; - } - - fLocked = true; - return B_OK; -} - - -bool -AddressSpaceReadLocker::Lock() -{ - if (fLocked) - return true; - if (fSpace == NULL) - return false; - - fSpace->ReadLock(); - fLocked = true; - - return true; -} - - -void -AddressSpaceReadLocker::Unlock() -{ - if (fLocked) { - fSpace->ReadUnlock(); - fLocked = false; - } -} - - -// #pragma mark - - - -AddressSpaceWriteLocker::AddressSpaceWriteLocker(team_id team) - : - fSpace(NULL), - fLocked(false), - fDegraded(false) -{ - SetTo(team); -} - - -AddressSpaceWriteLocker::AddressSpaceWriteLocker() - : - fSpace(NULL), - fLocked(false), - fDegraded(false) -{ -} - - -AddressSpaceWriteLocker::~AddressSpaceWriteLocker() -{ - Unset(); -} - - -void -AddressSpaceWriteLocker::Unset() -{ - Unlock(); - if (fSpace != NULL) - fSpace->Put(); -} - - -status_t -AddressSpaceWriteLocker::SetTo(team_id team) -{ - fSpace = VMAddressSpace::Get(team); - if (fSpace == NULL) - return B_BAD_TEAM_ID; - - fSpace->WriteLock(); - fLocked = true; - return B_OK; -} - - -status_t -AddressSpaceWriteLocker::SetFromArea(area_id areaID, VMArea*& area) -{ - fSpace = get_address_space_by_area_id(areaID); - if (fSpace == NULL) - return B_BAD_VALUE; - - fSpace->WriteLock(); - - area = VMAreaHash::Lookup(areaID); - - if (area == NULL || area->address_space != fSpace) { - fSpace->WriteUnlock(); - return B_BAD_VALUE; - } - - fLocked = true; - return B_OK; -} - - -status_t -AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID, - bool allowKernel, VMArea*& area) -{ - VMAreaHash::ReadLock(); - - area = VMAreaHash::LookupLocked(areaID); - if (area != NULL - && (area->address_space->ID() == team - || (allowKernel && team == VMAddressSpace::KernelID()))) { - fSpace = area->address_space; - fSpace->Get(); - } - - VMAreaHash::ReadUnlock(); - - if (fSpace == NULL) - return B_BAD_VALUE; - - // Second try to get the area -- this time with the address space - // write lock held - - fSpace->WriteLock(); - - area = VMAreaHash::Lookup(areaID); - - if (area == NULL) { - fSpace->WriteUnlock(); - return B_BAD_VALUE; - } - - fLocked = true; - return B_OK; -} - - -status_t -AddressSpaceWriteLocker::SetFromArea(team_id team, area_id areaID, - VMArea*& area) -{ - return SetFromArea(team, areaID, false, area); -} - - -void -AddressSpaceWriteLocker::Unlock() -{ - if (fLocked) { - if (fDegraded) - fSpace->ReadUnlock(); - else - fSpace->WriteUnlock(); - fLocked = false; - fDegraded = false; - } -} - - -void -AddressSpaceWriteLocker::DegradeToReadLock() -{ - // TODO: the current R/W lock implementation just keeps the write lock here - fSpace->ReadLock(); - fSpace->WriteUnlock(); - fDegraded = true; -} - - -// #pragma mark - - - -MultiAddressSpaceLocker::MultiAddressSpaceLocker() - : - fItems(NULL), - fCapacity(0), - fCount(0), - fLocked(false) -{ -} - - -MultiAddressSpaceLocker::~MultiAddressSpaceLocker() -{ - Unset(); - free(fItems); -} - - -/*static*/ int -MultiAddressSpaceLocker::_CompareItems(const void* _a, const void* _b) -{ - lock_item* a = (lock_item*)_a; - lock_item* b = (lock_item*)_b; - return a->space->ID() - b->space->ID(); -} - - -bool -MultiAddressSpaceLocker::_ResizeIfNeeded() -{ - if (fCount == fCapacity) { - lock_item* items = (lock_item*)realloc(fItems, - (fCapacity + 4) * sizeof(lock_item)); - if (items == NULL) - return false; - - fCapacity += 4; - fItems = items; - } - - return true; -} - - -int32 -MultiAddressSpaceLocker::_IndexOfAddressSpace(VMAddressSpace* space) const -{ - for (int32 i = 0; i < fCount; i++) { - if (fItems[i].space == space) - return i; - } - - return -1; -} - - -status_t -MultiAddressSpaceLocker::_AddAddressSpace(VMAddressSpace* space, - bool writeLock, VMAddressSpace** _space) -{ - if (!space) - return B_BAD_VALUE; - - int32 index = _IndexOfAddressSpace(space); - if (index < 0) { - if (!_ResizeIfNeeded()) { - space->Put(); - return B_NO_MEMORY; - } - - lock_item& item = fItems[fCount++]; - item.space = space; - item.write_lock = writeLock; - } else { - - // one reference is enough - space->Put(); - - fItems[index].write_lock |= writeLock; - } - - if (_space != NULL) - *_space = space; - - return B_OK; -} - - -inline status_t -MultiAddressSpaceLocker::AddTeam(team_id team, bool writeLock, - VMAddressSpace** _space) -{ - return _AddAddressSpace(VMAddressSpace::Get(team), writeLock, - _space); -} - - -inline status_t -MultiAddressSpaceLocker::AddArea(area_id area, bool writeLock, - VMAddressSpace** _space) -{ - return _AddAddressSpace(get_address_space_by_area_id(area), writeLock, - _space); -} - - -void -MultiAddressSpaceLocker::Unset() -{ - Unlock(); - - for (int32 i = 0; i < fCount; i++) - fItems[i].space->Put(); - - fCount = 0; -} - - -status_t -MultiAddressSpaceLocker::Lock() -{ - ASSERT(!fLocked); - - qsort(fItems, fCount, sizeof(lock_item), &_CompareItems); - - for (int32 i = 0; i < fCount; i++) { - status_t status; - if (fItems[i].write_lock) - status = fItems[i].space->WriteLock(); - else - status = fItems[i].space->ReadLock(); - - if (status < B_OK) { - while (--i >= 0) { - if (fItems[i].write_lock) - fItems[i].space->WriteUnlock(); - else - fItems[i].space->ReadUnlock(); - } - return status; - } - } - - fLocked = true; - return B_OK; -} - - -void -MultiAddressSpaceLocker::Unlock() -{ - if (!fLocked) - return; - - for (int32 i = 0; i < fCount; i++) { - if (fItems[i].write_lock) - fItems[i].space->WriteUnlock(); - else - fItems[i].space->ReadUnlock(); - } - - fLocked = false; -} - - -/*! Adds all address spaces of the areas associated with the given area's cache, - locks them, and locks the cache (including a reference to it). It retries - until the situation is stable (i.e. the neither cache nor cache's areas - changed) or an error occurs. -*/ -status_t -MultiAddressSpaceLocker::AddAreaCacheAndLock(area_id areaID, - bool writeLockThisOne, bool writeLockOthers, VMArea*& _area, - VMCache** _cache) -{ - // remember the original state - int originalCount = fCount; - lock_item* originalItems = NULL; - if (fCount > 0) { - originalItems = new(nothrow) lock_item[fCount]; - if (originalItems == NULL) - return B_NO_MEMORY; - memcpy(originalItems, fItems, fCount * sizeof(lock_item)); - } - ArrayDeleter _(originalItems); - - // get the cache - VMCache* cache; - VMArea* area; - status_t error; - { - AddressSpaceReadLocker locker; - error = locker.SetFromArea(areaID, area); - if (error != B_OK) - return error; - - cache = vm_area_get_locked_cache(area); - } - - while (true) { - // add all areas - VMArea* firstArea = cache->areas; - for (VMArea* current = firstArea; current; - current = current->cache_next) { - error = AddArea(current->id, - current == area ? writeLockThisOne : writeLockOthers); - if (error != B_OK) { - vm_area_put_locked_cache(cache); - return error; - } - } - - // unlock the cache and attempt to lock the address spaces - vm_area_put_locked_cache(cache); - - error = Lock(); - if (error != B_OK) - return error; - - // lock the cache again and check whether anything has changed - - // check whether the area is gone in the meantime - area = VMAreaHash::Lookup(areaID); - - if (area == NULL) { - Unlock(); - return B_BAD_VALUE; - } - - // lock the cache - VMCache* oldCache = cache; - cache = vm_area_get_locked_cache(area); - - // If neither the area's cache has changed nor its area list we're - // done. - if (cache == oldCache && firstArea == cache->areas) { - _area = area; - if (_cache != NULL) - *_cache = cache; - return B_OK; - } - - // Restore the original state and try again. - - // Unlock the address spaces, but keep the cache locked for the next - // iteration. - Unlock(); - - // Get an additional reference to the original address spaces. - for (int32 i = 0; i < originalCount; i++) - originalItems[i].space->Get(); - - // Release all references to the current address spaces. - for (int32 i = 0; i < fCount; i++) - fItems[i].space->Put(); - - // Copy over the original state. - fCount = originalCount; - if (originalItems != NULL) - memcpy(fItems, originalItems, fCount * sizeof(lock_item)); - } -} - - -// #pragma mark - - - #if VM_PAGE_FAULT_TRACING namespace VMPageFaultTracing { @@ -882,25 +265,6 @@ private: // #pragma mark - -static VMAddressSpace* -get_address_space_by_area_id(area_id id) -{ - VMAddressSpace* addressSpace = NULL; - - VMAreaHash::ReadLock(); - - VMArea* area = VMAreaHash::LookupLocked(id); - if (area != NULL) { - addressSpace = area->address_space; - addressSpace->Get(); - } - - VMAreaHash::ReadUnlock(); - - return addressSpace; -} - - //! You need to have the address space locked when calling this function static VMArea* lookup_area(VMAddressSpace* addressSpace, area_id id)