diff -ur linux.orig/include/asm-i386/kmap_types.h linux/include/asm-i386/kmap_types.h
--- linux.orig/include/asm-i386/kmap_types.h	Wed May  2 11:48:14 2001
+++ linux/include/asm-i386/kmap_types.h	Wed May  2 13:32:28 2001
@@ -6,6 +6,8 @@
 	KM_BOUNCE_WRITE,
 	KM_SKB_DATA,
 	KM_SKB_DATA_SOFTIRQ,
+	KM_USER0,
+	KM_USER1,
 	KM_TYPE_NR
 };
 
diff -ur linux.orig/include/linux/highmem.h linux/include/linux/highmem.h
--- linux.orig/include/linux/highmem.h	Mon Mar 26 18:48:11 2001
+++ linux/include/linux/highmem.h	Wed May  2 13:45:38 2001
@@ -45,8 +45,9 @@
 /* when CONFIG_HIGHMEM is not set these will be plain clear/copy_page */
 static inline void clear_user_highpage(struct page *page, unsigned long vaddr)
 {
-	clear_user_page(kmap(page), vaddr);
-	kunmap(page);
+	void *addr = kmap_atomic(page, KM_USER0);
+	clear_user_page(addr, vaddr);
+	kunmap_atomic(addr, KM_USER0);
 }
 
 static inline void clear_highpage(struct page *page)
@@ -85,11 +86,11 @@
 {
 	char *vfrom, *vto;
 
-	vfrom = kmap(from);
-	vto = kmap(to);
+	vfrom = kmap_atomic(from, KM_USER0);
+	vto = kmap_atomic(to, KM_USER1);
 	copy_user_page(vto, vfrom, vaddr);
-	kunmap(from);
-	kunmap(to);
+	kunmap_atomic(vfrom, KM_USER0);
+	kunmap_atomic(vto, KM_USER1);
 }
 
 static inline void copy_highpage(struct page *to, struct page *from)
diff -ur linux.orig/mm/filemap.c linux/mm/filemap.c
--- linux.orig/mm/filemap.c	Wed May  2 11:48:15 2001
+++ linux/mm/filemap.c	Wed May  2 12:31:21 2001
@@ -1799,7 +1799,12 @@
 	if (pte_present(pte) && ptep_test_and_clear_dirty(ptep)) {
 		struct page *page = pte_page(pte);
 		flush_tlb_page(vma, address);
+		/* yuck.  we have to drop the spinlock as set_page_dirty
+		 * can end up blocking.  -ben
+		 */
+		spin_unlock(&vma->vm_mm->page_table_lock);
 		set_page_dirty(page);
+		spin_lock(&vma->vm_mm->page_table_lock);
 	}
 	return 0;
 }
diff -ur linux.orig/mm/memory.c linux/mm/memory.c
--- linux.orig/mm/memory.c	Wed May  2 11:48:14 2001
+++ linux/mm/memory.c	Wed May  2 13:42:20 2001
@@ -238,8 +238,10 @@
 
 cont_copy_pte_range:		set_pte(dst_pte, pte);
 cont_copy_pte_range_noset:	address += PAGE_SIZE;
-				if (address >= end)
-					goto out_unlock;
+				if (address >= end) {
+					spin_unlock(&src->page_table_lock);
+					goto out;
+				}
 				src_pte++;
 				dst_pte++;
 			} while ((unsigned long)src_pte & PTE_TABLE_MASK);
@@ -249,8 +251,6 @@
 			dst_pmd++;
 		} while ((unsigned long)src_pmd & PMD_TABLE_MASK);
 	}
-out_unlock:
-	spin_unlock(&src->page_table_lock);
 out:
 	spin_unlock(&dst->page_table_lock);
 	return 0;
@@ -453,7 +453,7 @@
 	if (err)
 		return err;
 
-	down_write(&mm->mmap_sem);
+	down_read(&mm->mmap_sem);
 
 	err = -EFAULT;
 	iobuf->locked = 0;
@@ -524,12 +524,12 @@
 	}
 
 	spin_unlock(&mm->page_table_lock);
-	up_write(&mm->mmap_sem);
+	up_read(&mm->mmap_sem);
 	dprintk ("map_user_kiobuf: end OK\n");
 	return 0;
 
  out_unlock:
-	up_write(&mm->mmap_sem);
+	up_read(&mm->mmap_sem);
 	unmap_kiobuf(iobuf);
 	dprintk ("map_user_kiobuf: end %d\n", err);
 	return err;
@@ -869,7 +869,6 @@
 static inline void break_cow(struct vm_area_struct * vma, struct page *	old_page, struct page * new_page, unsigned long address, 
 		pte_t *page_table)
 {
-	copy_cow_page(old_page,new_page,address);
 	flush_page_to_ram(new_page);
 	flush_cache_page(vma, address);
 	establish_pte(vma, address, page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot))));
@@ -921,7 +920,7 @@
 	if (!VALID_PAGE(old_page))
 		goto bad_wp_page;
 
-	if (old_page == ZERO_PAGE(address))
+	if (old_page == ZERO_PAGE(address) || PageReserved(old_page))
 		goto copy;
 
 	/*
@@ -963,6 +962,13 @@
  	set_pte(page_table, pte);
 	spin_unlock(&mm->page_table_lock);
 	new_page = alloc_page(GFP_HIGHUSER);
+
+	/* Speculatively copy the page while not holding
+	 * the page table lock.  Make threads happy.  -ben
+	 */
+	if (new_page)
+		copy_cow_page(old_page, new_page, address);
+
 	spin_lock(&mm->page_table_lock);
 	if (!new_page)
 		return -1;
@@ -1203,6 +1209,8 @@
 		/* Allocate our own private page. */
 		spin_unlock(&mm->page_table_lock);
 		page = alloc_page(GFP_HIGHUSER);
+		if (page)
+			clear_user_highpage(page, addr);
 		spin_lock(&mm->page_table_lock);
 		if (!page)
 			return -1;
@@ -1211,7 +1219,6 @@
 			return 1;
 		}
 		mm->rss++;
-		clear_user_highpage(page, addr);
 		flush_page_to_ram(page);
 		entry = pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot)));
 	}
diff -ur linux.orig/mm/vmscan.c linux/mm/vmscan.c
--- linux.orig/mm/vmscan.c	Wed May  2 11:48:16 2001
+++ linux/mm/vmscan.c	Wed May  2 13:50:29 2001
@@ -66,6 +66,7 @@
 	if (PageSwapCache(page)) {
 		entry.val = page->index;
 		if (pte_dirty(pte))
+			/* FIXME: THIS MAY BLOCK on non-ext2 like fses */
 			set_page_dirty(page);
 set_swap_pte:
 		swap_duplicate(entry);
@@ -103,6 +104,7 @@
 	 * to its own backing store.
 	 */
 	if (page->mapping) {
+		/* FIXME: THIS MAY BLOCK on non-ext2 like fses */
 		set_page_dirty(page);
 		goto drop_pte;
 	}
@@ -119,6 +121,7 @@
 
 	/* Add it to the swap cache and mark it dirty */
 	add_to_swap_cache(page, entry);
+	/* FIXME: THIS MAY BLOCK on non-ext2 like fses */
 	set_page_dirty(page);
 	goto set_swap_pte;