[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: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Andrew Morton 2005-07-27 11:43:54 -07:00 committed by Linus Torvalds
parent 0cfc11ed45
commit 1aaf18ff9d

View File

@ -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. * Do a quick page-table lookup for a single page.
* mm->page_table_lock must be held. * mm->page_table_lock must be held.
*/ */
static struct page * static struct page *__follow_page(struct mm_struct *mm, unsigned long address,
__follow_page(struct mm_struct *mm, unsigned long address, int read, int write) int read, int write, int accessed)
{ {
pgd_t *pgd; pgd_t *pgd;
pud_t *pud; pud_t *pud;
@ -818,9 +818,11 @@ __follow_page(struct mm_struct *mm, unsigned long address, int read, int write)
pfn = pte_pfn(pte); pfn = pte_pfn(pte);
if (pfn_valid(pfn)) { if (pfn_valid(pfn)) {
page = pfn_to_page(pfn); page = pfn_to_page(pfn);
if (write && !pte_dirty(pte) && !PageDirty(page)) if (accessed) {
set_page_dirty(page); if (write && !pte_dirty(pte) &&!PageDirty(page))
mark_page_accessed(page); set_page_dirty(page);
mark_page_accessed(page);
}
return page; return page;
} }
} }
@ -829,16 +831,19 @@ __follow_page(struct mm_struct *mm, unsigned long address, int read, int write)
return NULL; return NULL;
} }
struct page * inline struct page *
follow_page(struct mm_struct *mm, unsigned long address, int write) 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); EXPORT_SYMBOL(check_user_page_readable);