Commit b7ae29f5 authored by Hugh Dickins's avatar Hugh Dickins Committed by Linus Torvalds

[PATCH] swapoff mmap_sem deadlock

Updating the mm lock ordering documentation drew attention to the fact that
we were wrong to blithely add down_read(&mm->mmap_sem) to swapoff's
unuse_process, while it holds swapcache page lock: not very likely, but it
could deadlock against, say, mlock faulting a page back in from swap.

But it looks like these days it's safe to drop and reacquire page lock if
down_read_trylock fails: the page lock is held to stop try_to_unmap
unmapping the page's ptes as fast as try_to_unuse is mapping them back in;
but the recent fix for get_user_pages works to prevent that too.
Signed-off-by: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5136181d
...@@ -481,6 +481,10 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma) ...@@ -481,6 +481,10 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma)
* an exclusive swap page, do_wp_page will replace it by a copy * an exclusive swap page, do_wp_page will replace it by a copy
* page, and the user never get to see the data GUP was holding * page, and the user never get to see the data GUP was holding
* the original page for. * the original page for.
*
* This test is also useful for when swapoff (unuse_process) has
* to drop page lock: its reference to the page stops existing
* ptes from being unmapped, so swapoff can make progress.
*/ */
if (PageSwapCache(page) && if (PageSwapCache(page) &&
page_count(page) != page->mapcount + 2) { page_count(page) != page->mapcount + 2) {
......
...@@ -548,7 +548,15 @@ static int unuse_process(struct mm_struct * mm, ...@@ -548,7 +548,15 @@ static int unuse_process(struct mm_struct * mm,
/* /*
* Go through process' page directory. * Go through process' page directory.
*/ */
down_read(&mm->mmap_sem); if (!down_read_trylock(&mm->mmap_sem)) {
/*
* Our reference to the page stops try_to_unmap_one from
* unmapping its ptes, so swapoff can make progress.
*/
unlock_page(page);
down_read(&mm->mmap_sem);
lock_page(page);
}
spin_lock(&mm->page_table_lock); spin_lock(&mm->page_table_lock);
for (vma = mm->mmap; vma; vma = vma->vm_next) { for (vma = mm->mmap; vma; vma = vma->vm_next) {
if (!is_vm_hugetlb_page(vma)) { if (!is_vm_hugetlb_page(vma)) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment