[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:
parent
0cfc11ed45
commit
1aaf18ff9d
25
mm/memory.c
25
mm/memory.c
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user