* vm_delete_areas(): Changed return type to void (was status_t and not used).

* _user_map_file(), _user_unmap_memory(): Verify that the address (if given) is
  page aligned.
* Reworked memory locking (wiring):
  - VMArea does now have a list of wired memory ranges and supports waiting for
    a range to be removed.
  - vm_soft_fault():
    - Added "wirePage" parameter that, if given, makes the function wire the
      page and return it.
    - Added "wiredRange" parameter (for calls from lock_memory_etc()) and made
      sure we never unmap wired pages. This could e.g. happen when a page from a
      lower cache was read-mapped and a write fault occurred. Now in such a
      situation the function waits for the page to be unwired and restarts.
  - All functions that manipulate areas in a way that could affect wired ranges
    do now either require the caller to make sure there are no wired ranges in
    the way or do that themselves. Added a few wait_if_*_is_wired() helper
    functions for that purpose.
  - lock_memory_etc():
    - Does now also work correctly when the range spans more than one area.
    - Adds VMAreaWiredRanges to the affected VMAreas and retains an address
      space reference (so that the address space won't be deleted as long as a
      wired range exists).
    - Resolved TODO: The area's caches are now locked when
      increment_page_wired_count() is called.
    - Resolved TODO: The race condition due to missing locking after looking up
      the page mapping is now prevented. We hold the cache locks (in case the
      page is already mapped) and the new vm_soft_fault() parameter allows us
      to get the page wired.
  - unlock_memory_etc(): Changes symmetrical to those in lock_memory_etc() and
    resolved all TODOs.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36030 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-04-03 18:01:29 +00:00
parent 21ff565f76
commit 550376ffb8
4 changed files with 845 additions and 242 deletions
+1 -2
View File
@@ -198,8 +198,7 @@ VMAddressSpace::GetAreaIterator()
extern "C" { extern "C" {
#endif #endif
status_t vm_delete_areas(struct VMAddressSpace *aspace, void vm_delete_areas(struct VMAddressSpace *aspace, bool deletingAddressSpace);
bool deletingAddressSpace);
#define vm_swap_address_space(from, to) arch_vm_aspace_swap(from, to) #define vm_swap_address_space(from, to) arch_vm_aspace_swap(from, to)
#ifdef __cplusplus #ifdef __cplusplus
+56
View File
@@ -12,6 +12,7 @@
#include <lock.h> #include <lock.h>
#include <util/DoublyLinkedList.h> #include <util/DoublyLinkedList.h>
#include <util/SinglyLinkedList.h>
#include <util/OpenHashTable.h> #include <util/OpenHashTable.h>
#include <vm/vm_types.h> #include <vm/vm_types.h>
@@ -22,6 +23,46 @@ struct VMKernelAddressSpace;
struct VMUserAddressSpace; struct VMUserAddressSpace;
struct VMAreaUnwiredWaiter
: public DoublyLinkedListLinkImpl<VMAreaUnwiredWaiter> {
VMArea* area;
addr_t base;
size_t size;
ConditionVariable condition;
ConditionVariableEntry waitEntry;
};
typedef DoublyLinkedList<VMAreaUnwiredWaiter> VMAreaUnwiredWaiterList;
struct VMAreaWiredRange : SinglyLinkedListLinkImpl<VMAreaWiredRange> {
VMArea* area;
addr_t base;
size_t size;
bool writable;
bool implicit; // range created automatically
VMAreaUnwiredWaiterList waiters;
VMAreaWiredRange(addr_t base, size_t size, bool writable, bool implicit)
:
area(NULL),
base(base),
size(size),
writable(writable),
implicit(implicit)
{
}
bool IntersectsWith(addr_t base, size_t size) const
{
return this->base + this->size - 1 >= base
&& base + size - 1 >= this->base;
}
};
typedef SinglyLinkedList<VMAreaWiredRange> VMAreaWiredRangeList;
struct VMArea { struct VMArea {
char* name; char* name;
area_id id; area_id id;
@@ -48,6 +89,20 @@ struct VMArea {
{ return address >= fBase { return address >= fBase
&& address <= fBase + (fSize - 1); } && address <= fBase + (fSize - 1); }
bool IsWired() const
{ return !fWiredRanges.IsEmpty(); }
bool IsWired(addr_t base, size_t size) const;
void Wire(VMAreaWiredRange* range);
VMAreaWiredRange* Wire(addr_t base, size_t size, bool writable);
void Unwire(VMAreaWiredRange* range);
void Unwire(addr_t base, size_t size, bool writable);
bool AddWaiterIfWired(VMAreaUnwiredWaiter* waiter);
bool AddWaiterIfWired(VMAreaUnwiredWaiter* waiter,
addr_t base, size_t size,
VMAreaWiredRange* ignoreRange = NULL);
protected: protected:
VMArea(VMAddressSpace* addressSpace, VMArea(VMAddressSpace* addressSpace,
uint32 wiring, uint32 protection); uint32 wiring, uint32 protection);
@@ -67,6 +122,7 @@ protected:
protected: protected:
addr_t fBase; addr_t fBase;
size_t fSize; size_t fSize;
VMAreaWiredRangeList fWiredRanges;
}; };
+157 -1
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2009, Ingo Weinhold, [email protected]. * Copyright 2009-2010, Ingo Weinhold, [email protected].
* Copyright 2002-2009, Axel Dörfler, [email protected]. * Copyright 2002-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
@@ -13,6 +13,7 @@
#include <new> #include <new>
#include <heap.h> #include <heap.h>
#include <vm/VMAddressSpace.h>
#define AREA_HASH_TABLE_SIZE 1024 #define AREA_HASH_TABLE_SIZE 1024
@@ -75,6 +76,161 @@ VMArea::Init(const char* name, uint32 allocationFlags)
} }
/*! Returns whether any part of the given address range intersects with a wired
range of this area.
The area's top cache must be locked.
*/
bool
VMArea::IsWired(addr_t base, size_t size) const
{
for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
VMAreaWiredRange* range = it.Next();) {
if (range->IntersectsWith(base, size))
return true;
}
return false;
}
/*! Adds the given wired range to this area.
The area's top cache must be locked.
*/
void
VMArea::Wire(VMAreaWiredRange* range)
{
ASSERT(range->area == NULL);
range->area = this;
fWiredRanges.Add(range);
}
/*! Adds a wired range to this area.
The area's top cache must be locked.
\return The newly created wired area object. \c NULL when out of memory.
*/
VMAreaWiredRange*
VMArea::Wire(addr_t base, size_t size, bool writable)
{
VMAreaWiredRange* range = new(std::nothrow) VMAreaWiredRange(base, size,
writable, true);
if (range == NULL)
return NULL;
Wire(range);
return range;
}
/*! Removes the given wired range from this area.
Must balance a previous Wire() call.
The area's top cache must be locked.
*/
void
VMArea::Unwire(VMAreaWiredRange* range)
{
ASSERT(range->area == this);
// remove the range
range->area = NULL;
fWiredRanges.Remove(range);
// wake up waiters
for (VMAreaUnwiredWaiterList::Iterator it = range->waiters.GetIterator();
VMAreaUnwiredWaiter* waiter = it.Next();) {
waiter->condition.NotifyAll();
}
range->waiters.MakeEmpty();
}
/*! Removes a wired range from this area.
Must balance a previous Wire() call.
The area's top cache must be locked.
*/
void
VMArea::Unwire(addr_t base, size_t size, bool writable)
{
for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
VMAreaWiredRange* range = it.Next();) {
if (range->implicit && range->base == base && range->size == size
&& range->writable == writable) {
Unwire(range);
delete range;
return;
}
}
panic("VMArea::Unwire(%#" B_PRIxADDR ", %#" B_PRIxADDR ", %d): no such "
"range", base, size, writable);
}
/*! If the area has any wired range, the given waiter is added to the range and
prepared for waiting.
\return \c true, if the waiter has been added, \c false otherwise.
*/
bool
VMArea::AddWaiterIfWired(VMAreaUnwiredWaiter* waiter)
{
VMAreaWiredRange* range = fWiredRanges.Head();
if (range == NULL)
return false;
waiter->area = this;
waiter->base = fBase;
waiter->size = fSize;
waiter->condition.Init(this, "area unwired");
waiter->condition.Add(&waiter->waitEntry);
range->waiters.Add(waiter);
return true;
}
/*! If the given address range intersect with a wired range of this area, the
given waiter is added to the range and prepared for waiting.
\param waiter The waiter structure that will be added to the wired range
that intersects with the given address range.
\param base The base of the address range to check.
\param size The size of the address range to check.
\param ignoreRange If given, this wired range of the area is not checked
whether it intersects with the given address range. Useful when the
caller has added the range and only wants to check intersection with
other ranges.
\return \c true, if the waiter has been added, \c false otherwise.
*/
bool
VMArea::AddWaiterIfWired(VMAreaUnwiredWaiter* waiter, addr_t base, size_t size,
VMAreaWiredRange* ignoreRange)
{
for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
VMAreaWiredRange* range = it.Next();) {
if (range != ignoreRange && range->IntersectsWith(base, size)) {
waiter->area = this;
waiter->base = base;
waiter->size = size;
waiter->condition.Init(this, "area unwired");
waiter->condition.Add(&waiter->waitEntry);
range->waiters.Add(waiter);
return true;
}
}
return false;
}
// #pragma mark - VMAreaHash // #pragma mark - VMAreaHash
File diff suppressed because it is too large Load Diff