forked from Minki/linux
mm: wp lock page before deciding cow
An application may rely on get_user_pages() to give it pages writable from userspace and shared with a driver, GUP breaking COW if necessary. It may mprotect() the pages' writability, off and on, from time to time. Normally this works fine (so long as the app does not fork); but just occasionally, under memory pressure, a readonly pte in a newly writable area is COWed unnecessarily, breaking the link with the driver: because do_wp_page() does trylock_page, and falls back to COW whenever that fails. For reliable behaviour in the unshared case, when the trylock_page fails, now unlock pagetable, lock page and relock pagetable, before deciding whether Copy-On-Write is really necessary. Reported-by: Zhou Yingchao Signed-off-by: Hugh Dickins <hugh@veritas.com> Cc: Lee Schermerhorn <lee.schermerhorn@hp.com> Cc: Rik van Riel <riel@redhat.com> Cc: Nick Piggin <nickpiggin@yahoo.com.au> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Robin Holt <holt@sgi.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
878b63ac88
commit
ab967d8601
17
mm/memory.c
17
mm/memory.c
@ -1848,10 +1848,21 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
* not dirty accountable.
|
||||
*/
|
||||
if (PageAnon(old_page)) {
|
||||
if (trylock_page(old_page)) {
|
||||
reuse = can_share_swap_page(old_page);
|
||||
unlock_page(old_page);
|
||||
if (!trylock_page(old_page)) {
|
||||
page_cache_get(old_page);
|
||||
pte_unmap_unlock(page_table, ptl);
|
||||
lock_page(old_page);
|
||||
page_table = pte_offset_map_lock(mm, pmd, address,
|
||||
&ptl);
|
||||
if (!pte_same(*page_table, orig_pte)) {
|
||||
unlock_page(old_page);
|
||||
page_cache_release(old_page);
|
||||
goto unlock;
|
||||
}
|
||||
page_cache_release(old_page);
|
||||
}
|
||||
reuse = can_share_swap_page(old_page);
|
||||
unlock_page(old_page);
|
||||
} else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) ==
|
||||
(VM_WRITE|VM_SHARED))) {
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user