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
This commit is contained in:
@@ -9,6 +9,7 @@ KernelMergeObject kernel_vm.o :
|
||||
vm_daemons.cpp
|
||||
vm_page.cpp
|
||||
VMAddressSpace.cpp
|
||||
VMAddressSpaceLocking.cpp
|
||||
VMAnonymousCache.cpp
|
||||
VMAnonymousNoSwapCache.cpp
|
||||
VMArea.cpp
|
||||
|
||||
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Copyright 2002-2009, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
|
||||
#include "VMAddressSpaceLocking.h"
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/VMAddressSpace.h>
|
||||
#include <vm/VMArea.h>
|
||||
#include <vm/VMCache.h>
|
||||
|
||||
|
||||
// #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<lock_item> _(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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Copyright 2002-2009, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef VM_ADDRESS_SPACE_LOCKING_H
|
||||
#define VM_ADDRESS_SPACE_LOCKING_H
|
||||
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <vm/VMAddressSpace.h>
|
||||
|
||||
|
||||
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
|
||||
+1
-637
@@ -50,6 +50,7 @@
|
||||
#include <vm/VMArea.h>
|
||||
#include <vm/VMCache.h>
|
||||
|
||||
#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<lock_item> _(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)
|
||||
|
||||
Reference in New Issue
Block a user