From: William Lee Irwin III <wli@holomorphy.com>

vma->vm_ops->page_mkwrite() is supposed to be able to sleep, but this
function doesn't unmap the pte across the call and worse yet, reuses the
pte across a drop and reacquisition of ->page_table_lock.

Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/mm/memory.c |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)

diff -puN mm/memory.c~add-page-becoming-writable-notification-fix mm/memory.c
--- 25/mm/memory.c~add-page-becoming-writable-notification-fix	2005-03-10 19:09:43.000000000 -0800
+++ 25-akpm/mm/memory.c	2005-03-10 19:09:43.000000000 -0800
@@ -1142,6 +1142,7 @@ static inline void break_cow(struct vm_a
 static inline int do_wp_page_mk_pte_writable(struct mm_struct *mm,
 					     struct vm_area_struct *vma,
 					     unsigned long address,
+					     pmd_t *pmd,
 					     pte_t *page_table,
 					     struct page *old_page,
 					     pte_t pte)
@@ -1153,6 +1154,7 @@ static inline int do_wp_page_mk_pte_writ
 	if (vma->vm_ops && vma->vm_ops->page_mkwrite) {
 		/* Notify the page owner without the lock held so they can
 		 * sleep if they want to */
+		pte_unmap(page_table);
 		spin_unlock(&mm->page_table_lock);
 
 		if (vma->vm_ops->page_mkwrite(vma, old_page) < 0)
@@ -1165,11 +1167,12 @@ static inline int do_wp_page_mk_pte_writ
 		 * return, as we can count on the MMU to tell us if they didn't
 		 * also make it writable
 		 */
+		page_table = pte_offset_map(pmd, address);
 		if (!pte_same(*page_table, pte))
 			goto minor_fault;
 	}
 
-	flush_cache_page(vma, address);
+	flush_cache_page(vma, address, page_to_pfn(old_page));
 	entry = maybe_mkwrite(pte_mkyoung(pte_mkdirty(pte)),
 			      vma);
 	ptep_set_access_flags(vma, address, page_table, entry, 1);
@@ -1229,7 +1232,7 @@ static int do_wp_page(struct mm_struct *
 		unlock_page(old_page);
 		if (reuse) {
 			/* We can just make the PTE writable */
-			return do_wp_page_mk_pte_writable(mm, vma, address,
+			return do_wp_page_mk_pte_writable(mm, vma, address, pmd,
 							  page_table, old_page,
 							  pte);
 		}
_