Commit 1aaf18ff authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] check_user_page_readable() deadlock fix

Fix bug identifued by Richard Purdie <rpurdie@rpsys.net>.

oprofile calls check_user_page_readable() from interrupt context, so we
deadlock over various VFS locks.

But check_user_page_readable() doesn't imply either a read or a write of the
page's contents.  Change __follow_page() so that check_user_page_readable()
can tell __follow_page() that we're not accessing the page's contents, and use
that info to avoid the troublesome lock-takings.

Also, make follow_page() inline for the single callsite in memory.c to save a
bit of stack space.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 0cfc11ed
......@@ -776,8 +776,8 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address,
* Do a quick page-table lookup for a single page.
* mm->page_table_lock must be held.
*/
static struct page *
__follow_page(struct mm_struct *mm, unsigned long address, int read, int write)
static struct page *__follow_page(struct mm_struct *mm, unsigned long address,
int read, int write, int accessed)
{
pgd_t *pgd;
pud_t *pud;
......@@ -818,9 +818,11 @@ __follow_page(struct mm_struct *mm, unsigned long address, int read, int write)
pfn = pte_pfn(pte);
if (pfn_valid(pfn)) {
page = pfn_to_page(pfn);
if (write && !pte_dirty(pte) && !PageDirty(page))
if (accessed) {
if (write && !pte_dirty(pte) &&!PageDirty(page))
set_page_dirty(page);
mark_page_accessed(page);
}
return page;
}
}
......@@ -829,16 +831,19 @@ out:
return NULL;
}
struct page *
inline struct page *
follow_page(struct mm_struct *mm, unsigned long address, int write)
{
return __follow_page(mm, address, /*read*/0, write);
return __follow_page(mm, address, 0, write, 1);
}
int
check_user_page_readable(struct mm_struct *mm, unsigned long address)
/*
* check_user_page_readable() can be called frm niterrupt context by oprofile,
* so we need to avoid taking any non-irq-safe locks
*/
int check_user_page_readable(struct mm_struct *mm, unsigned long address)
{
return __follow_page(mm, address, /*read*/1, /*write*/0) != NULL;
return __follow_page(mm, address, 1, 0, 0) != NULL;
}
EXPORT_SYMBOL(check_user_page_readable);
......
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