From 078a965f65b5f56ecb3f0c72fc97a36238509ca8 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Wed, 29 Oct 2014 12:32:37 +0100 Subject: [PATCH] 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. --- headers/private/kernel/vm/VMArea.h | 11 +++++++++-- src/system/kernel/vm/VMArea.cpp | 13 +++++++------ src/system/kernel/vm/vm.cpp | 8 ++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/headers/private/kernel/vm/VMArea.h b/headers/private/kernel/vm/VMArea.h index 669c98aa70..7c4621ce85 100644 --- a/headers/private/kernel/vm/VMArea.h +++ b/headers/private/kernel/vm/VMArea.h @@ -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, diff --git a/src/system/kernel/vm/VMArea.cpp b/src/system/kernel/vm/VMArea.cpp index b87b1d6ea9..ffe111e13f 100644 --- a/src/system/kernel/vm/VMArea.cpp +++ b/src/system/kernel/vm/VMArea.cpp @@ -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; diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index 4c65cae963..5253a58ab7 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -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