vm_soft_fault(): Avoid deadlock waiting for wired ranges

* VMArea::AddWaiterIfWired(): Replace the ignoreRange argument by a
flags argument and introduce (currently only) flag
IGNORE_WRITE_WIRED_RANGES. If specified, ranges wired for writing
are ignored. Ignoring just a single specified range doesn't cut it
in vm_soft_fault(), and there aren't any other users of that feature.
* vm_soft_fault(): When having to unmap a page of a lower cache, this
page cannot be wired for writing. So we can safely ignore all
writed-wired ranges, instead of just our own. We even have to do that
in case there's another thread that concurrently tries to write-wire
the same page, since otherwise we'd deadlock waiting for each other.
This commit is contained in:
Ingo Weinhold
2014-10-29 12:37:25 +01:00
parent cf3c703b7a
commit 078a965f65
3 changed files with 22 additions and 10 deletions
+9 -2
View File
@@ -89,6 +89,14 @@ struct VMPageWiringInfo {
struct VMArea {
public:
enum {
// AddWaiterIfWired() flags
IGNORE_WRITE_WIRED_RANGES = 0x01, // ignore existing ranges that
// wire for writing
};
public:
char* name;
area_id id;
uint32 protection;
@@ -130,8 +138,7 @@ public:
bool AddWaiterIfWired(VMAreaUnwiredWaiter* waiter);
bool AddWaiterIfWired(VMAreaUnwiredWaiter* waiter,
addr_t base, size_t size,
VMAreaWiredRange* ignoreRange = NULL);
addr_t base, size_t size, uint32 flags = 0);
protected:
VMArea(VMAddressSpace* addressSpace,
+7 -6
View File
@@ -185,19 +185,20 @@ VMArea::AddWaiterIfWired(VMAreaUnwiredWaiter* waiter)
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.
\param flags
- \c IGNORE_WRITE_WIRED_RANGES: Ignore ranges wired for writing.
\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)
uint32 flags)
{
for (VMAreaWiredRangeList::Iterator it = fWiredRanges.GetIterator();
VMAreaWiredRange* range = it.Next();) {
if (range != ignoreRange && range->IntersectsWith(base, size)) {
if ((flags & IGNORE_WRITE_WIRED_RANGES) != 0 && range->writable)
continue;
if (range->IntersectsWith(base, size)) {
waiter->area = this;
waiter->base = base;
waiter->size = size;
+6 -2
View File
@@ -4681,10 +4681,14 @@ vm_soft_fault(VMAddressSpace* addressSpace, addr_t originalAddress,
if (unmapPage) {
// If the page is wired, we can't unmap it. Wait until it is unwired
// again and restart.
// again and restart. Note that the page cannot be wired for
// writing, since it it isn't in the topmost cache. So we can safely
// ignore ranges wired for writing (our own and other concurrent
// wiring attempts in progress) and in fact have to do that to avoid
// a deadlock.
VMAreaUnwiredWaiter waiter;
if (area->AddWaiterIfWired(&waiter, address, B_PAGE_SIZE,
wiredRange)) {
VMArea::IGNORE_WRITE_WIRED_RANGES)) {
// unlock everything and wait
if (context.pageAllocated) {
// ... but since we allocated a page and inserted it into