Do not held the vm_map lock while flushing pages in msync(2) and madvise(2).
authormpi <mpi@openbsd.org>
Fri, 24 Feb 2023 15:17:48 +0000 (15:17 +0000)
committermpi <mpi@openbsd.org>
Fri, 24 Feb 2023 15:17:48 +0000 (15:17 +0000)
commitb5b36f0ff309f84c94d496dda0350e7c0ae91391
treedd53d70e8dc1eadd29920c6875041eed722ca6f0
parenta4f11372d5ec16405c3947a49e9200b89358d82d
Do not held the vm_map lock while flushing pages in msync(2) and madvise(2).

Mark the VM map as busy instead to prevent any sibling thread to request an
exclusive version of the vm_map.  This is necessary to let any PG_BUSY page,
found in the UVM vnode object, to be released by a sibling in the middle of
a page-fault.

Note: the page-fault handler releases & re-grab a shared version of the
vm_map lock and expect it to be available to make progress.

Prevent a 3-Threads deadlock between msync(2), page-fault and mmap(2).  The
deadlock reported on bugs@ by many occured as follow:

..ThreadA faults & grabs the shared `vmmaplk' then release it before calling
..uvn_get() which might sleep to allocate pages and mark them as PG_BUSY.

..Once the lock is released, threadB calls uvn_flush().  It sees at least a
..PG_BUSY page and sleeps on the `vmmaplk' waiting for threadA to un-busy
..the page.

..At the same time threadC asked for an exclusive version of the lock and
..sleeps until all reader are done with it.  This prevents threadA to
..acquire a shared-version of the lock and finish the page fault.

This issue is similar to NetBSD's PR #56952 and the fix is from Chuck Silvers.

Tested by many on bugs@, thanks!

ok kettenis@
sys/uvm/uvm_map.c