aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugh Dickins <hugh@veritas.com>2009-01-06 17:39:33 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-01-06 18:59:03 -0500
commitab967d86015a19777955370deebc8262d50fed63 (patch)
treebe0807bef32c0d28ac6dbcf7c8b938840d2f2a3f
parent878b63ac889df706d01048f2c110e322ad2f996d (diff)
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>
-rw-r--r--mm/memory.c17
1 files changed, 14 insertions, 3 deletions
diff --git a/mm/memory.c b/mm/memory.c
index f594bb65a9f1..3922ffcf3dff 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1848,10 +1848,21 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
1848 * not dirty accountable. 1848 * not dirty accountable.
1849 */ 1849 */
1850 if (PageAnon(old_page)) { 1850 if (PageAnon(old_page)) {
1851 if (trylock_page(old_page)) { 1851 if (!trylock_page(old_page)) {
1852 reuse = can_share_swap_page(old_page); 1852 page_cache_get(old_page);
1853 unlock_page(old_page); 1853 pte_unmap_unlock(page_table, ptl);
1854 lock_page(old_page);
1855 page_table = pte_offset_map_lock(mm, pmd, address,
1856 &ptl);
1857 if (!pte_same(*page_table, orig_pte)) {
1858 unlock_page(old_page);
1859 page_cache_release(old_page);
1860 goto unlock;
1861 }
1862 page_cache_release(old_page);
1854 } 1863 }
1864 reuse = can_share_swap_page(old_page);
1865 unlock_page(old_page);
1855 } else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) == 1866 } else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) ==
1856 (VM_WRITE|VM_SHARED))) { 1867 (VM_WRITE|VM_SHARED))) {
1857 /* 1868 /*