Index: arch/ia64/src/mm/tlb.c
===================================================================
--- arch/ia64/src/mm/tlb.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ arch/ia64/src/mm/tlb.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -424,4 +424,5 @@
 	
 	va = istate->cr_ifa;	/* faulting address */
+	page_table_lock(AS, true);
 	t = page_mapping_find(AS, va);
 	if (t) {
@@ -431,9 +432,12 @@
 		 */
 		itc_pte_copy(t);
+		page_table_unlock(AS, true);
 	} else {
 		/*
 		 * Forward the page fault to address space page fault handler.
 		 */
+		page_table_unlock(AS, true);
 		if (!as_page_fault(va)) {
+			page_table_unlock(AS, true);
 			panic("%s: va=%P, rid=%d\n", __FUNCTION__, istate->cr_ifa, rr.map.rid);
 		}
@@ -467,4 +471,5 @@
 	}
 
+	page_table_lock(AS, true);
 	t = page_mapping_find(AS, va);
 	if (t) {
@@ -474,8 +479,10 @@
 		 */
 		dtc_pte_copy(t);
+		page_table_unlock(AS, true);
 	} else {
 		/*
 		 * Forward the page fault to address space page fault handler.
 		 */
+		page_table_unlock(AS, true);
 		if (!as_page_fault(va)) {
 			panic("%s: va=%P, rid=%d, iip=%P\n", __FUNCTION__, va, rid, istate->cr_iip);
@@ -505,4 +512,5 @@
 	pte_t *t;
 
+	page_table_lock(AS, true);
 	t = page_mapping_find(AS, istate->cr_ifa);
 	ASSERT(t && t->p);
@@ -515,4 +523,5 @@
 		dtc_pte_copy(t);
 	}
+	page_table_unlock(AS, true);
 }
 
@@ -526,4 +535,5 @@
 	pte_t *t;
 
+	page_table_lock(AS, true);
 	t = page_mapping_find(AS, istate->cr_ifa);
 	ASSERT(t && t->p);
@@ -536,4 +546,5 @@
 		itc_pte_copy(t);
 	}
+	page_table_unlock(AS, true);
 }
 
@@ -547,4 +558,5 @@
 	pte_t *t;
 
+	page_table_lock(AS, true);
 	t = page_mapping_find(AS, istate->cr_ifa);
 	ASSERT(t && t->p);
@@ -557,4 +569,5 @@
 		dtc_pte_copy(t);
 	}
+	page_table_unlock(AS, true);
 }
 
@@ -571,4 +584,5 @@
 	
 	va = istate->cr_ifa;	/* faulting address */
+	page_table_lock(AS, true);
 	t = page_mapping_find(AS, va);
 	ASSERT(t);
@@ -583,5 +597,7 @@
 		else
 			dtc_pte_copy(t);
+		page_table_unlock(AS, true);
 	} else {
+		page_table_unlock(AS, true);
 		if (!as_page_fault(va)) {
 			panic("%s: va=%P, rid=%d\n", __FUNCTION__, va, rr.map.rid);
Index: arch/mips32/src/mm/tlb.c
===================================================================
--- arch/mips32/src/mm/tlb.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ arch/mips32/src/mm/tlb.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -88,5 +88,6 @@
 {
 	entry_lo_t lo;
-	entry_hi_t hi;	
+	entry_hi_t hi;
+	asid_t asid;
 	__address badvaddr;
 	pte_t *pte;
@@ -94,5 +95,9 @@
 	badvaddr = cp0_badvaddr_read();
 
-	spinlock_lock(&AS->lock);		
+	spinlock_lock(&AS->lock);
+	asid = AS->asid;
+	spinlock_unlock(&AS->lock);
+
+	page_table_lock(AS, true);
 
 	pte = find_mapping_and_check(badvaddr);
@@ -105,5 +110,5 @@
 	pte->a = 1;
 
-	prepare_entry_hi(&hi, AS->asid, badvaddr);
+	prepare_entry_hi(&hi, asid, badvaddr);
 	prepare_entry_lo(&lo, pte->g, pte->p, pte->d, pte->cacheable, pte->pfn);
 
@@ -123,9 +128,9 @@
 	tlbwr();
 
-	spinlock_unlock(&AS->lock);
+	page_table_unlock(AS, true);
 	return;
 	
 fail:
-	spinlock_unlock(&AS->lock);
+	page_table_unlock(AS, true);
 	tlb_refill_fail(istate);
 }
@@ -155,6 +160,6 @@
 	tlbp();
 	index.value = cp0_index_read();
-	
-	spinlock_lock(&AS->lock);	
+
+	page_table_lock(AS, true);	
 	
 	/*
@@ -192,9 +197,9 @@
 	tlbwi();
 
-	spinlock_unlock(&AS->lock);	
+	page_table_unlock(AS, true);
 	return;
 	
 fail:
-	spinlock_unlock(&AS->lock);
+	page_table_unlock(AS, true);
 	tlb_invalid_fail(istate);
 }
@@ -224,6 +229,6 @@
 	tlbp();
 	index.value = cp0_index_read();
-	
-	spinlock_lock(&AS->lock);	
+
+	page_table_lock(AS, true);	
 	
 	/*
@@ -268,9 +273,9 @@
 	tlbwi();
 
-	spinlock_unlock(&AS->lock);	
+	page_table_unlock(AS, true);
 	return;
 	
 fail:
-	spinlock_unlock(&AS->lock);
+	page_table_unlock(AS, true);
 	tlb_modified_fail(istate);
 }
@@ -350,4 +355,5 @@
 		 * Resort to higher-level page fault handler.
 		 */
+		page_table_unlock(AS, true);
 		if (as_page_fault(badvaddr)) {
 			/*
@@ -355,27 +361,15 @@
 			 * The mapping ought to be in place.
 			 */
+			page_table_lock(AS, true);
 			pte = page_mapping_find(AS, badvaddr);
 			ASSERT(pte && pte->p);
 			return pte;
+		} else {
+			page_table_lock(AS, true);
+			printf("Page fault.\n");
+			return NULL;
 		}
-	}
-
-	/*
-	 * Handler cannot succeed if badvaddr has no mapping.
-	 */
-	if (!pte) {
-		printf("No such mapping.\n");
-		return NULL;
-	}
-
-	/*
-	 * Handler cannot succeed if the mapping is marked as invalid.
-	 */
-	if (!pte->p) {
-		printf("Invalid mapping.\n");
-		return NULL;
-	}
-
-	return pte;
+		
+	}
 }
 
Index: genarch/src/mm/as_ht.c
===================================================================
--- genarch/src/mm/as_ht.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ genarch/src/mm/as_ht.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -35,9 +35,15 @@
 #include <memstr.h>
 #include <adt/hash_table.h>
+#include <synch/spinlock.h>
 
 static pte_t *ht_create(int flags);
 
+static void ht_lock(as_t *as, bool lock);
+static void ht_unlock(as_t *as, bool unlock);
+
 as_operations_t as_ht_operations = {
-	.page_table_create = ht_create
+	.page_table_create = ht_create,
+	.page_table_lock = ht_lock,
+	.page_table_unlock = ht_unlock,
 };
 
@@ -59,2 +65,32 @@
 	return NULL;
 }
+
+/** Lock page table.
+ *
+ * Lock address space and page hash table.
+ * Interrupts must be disabled.
+ *
+ * @param as Address space.
+ * @param lock If false, do not attempt to lock the address space.
+ */
+void ht_lock(as_t *as, bool lock)
+{
+	if (lock)
+		spinlock_lock(&as->lock);
+	spinlock_lock(&page_ht_lock);
+}
+
+/** Unlock page table.
+ *
+ * Unlock address space and page hash table.
+ * Interrupts must be disabled.
+ *
+ * @param as Address space.
+ * @param unlock If false, do not attempt to lock the address space.
+ */
+void ht_unlock(as_t *as, bool unlock)
+{
+	spinlock_unlock(&page_ht_lock);
+	if (unlock)
+		spinlock_unlock(&as->lock);
+}
Index: genarch/src/mm/as_pt.c
===================================================================
--- genarch/src/mm/as_pt.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ genarch/src/mm/as_pt.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -40,6 +40,11 @@
 static pte_t *ptl0_create(int flags);
 
+static void pt_lock(as_t *as, bool lock);
+static void pt_unlock(as_t *as, bool unlock);
+
 as_operations_t as_pt_operations = {
-	.page_table_create = ptl0_create
+	.page_table_create = ptl0_create,
+	.page_table_lock = pt_lock,
+	.page_table_unlock = pt_unlock
 };
 
@@ -77,2 +82,30 @@
 	return (pte_t *) KA2PA((__address) dst_ptl0);
 }
+
+/** Lock page tables.
+ *
+ * Lock only the address space.
+ * Interrupts must be disabled.
+ *
+ * @param as Address space.
+ * @param lock If false, do not attempt to lock the address space.
+ */
+void pt_lock(as_t *as, bool lock)
+{
+	if (lock)
+		spinlock_lock(&as->lock);
+}
+
+/** Unlock page tables.
+ *
+ * Unlock the address space.
+ * Interrupts must be disabled.
+ *
+ * @param as Address space.
+ * @param unlock If false, do not attempt to unlock the address space.
+ */
+void pt_unlock(as_t *as, bool unlock)
+{
+	if (unlock)
+		spinlock_unlock(&as->lock);
+}
Index: genarch/src/mm/page_ht.c
===================================================================
--- genarch/src/mm/page_ht.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ genarch/src/mm/page_ht.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -53,5 +53,7 @@
 
 /**
- * This lock protects the page hash table.
+ * This lock protects the page hash table. It must be acquired
+ * after address space lock and after any address space area
+ * locks.
  */
 SPINLOCK_INITIALIZE(page_ht_lock);
@@ -156,5 +158,5 @@
  * using 'flags'. 
  *
- * The address space must be locked and interruptsmust be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to which page belongs.
@@ -168,6 +170,4 @@
 	__native key[2] = { (__address) as, page = ALIGN_DOWN(page, PAGE_SIZE) };
 	
-	spinlock_lock(&page_ht_lock);
-
 	if (!hash_table_find(&page_ht, key)) {
 		t = (pte_t *) malloc(sizeof(pte_t), FRAME_ATOMIC);
@@ -187,6 +187,4 @@
 		hash_table_insert(&page_ht, key, &t->link);
 	}
-	
-	spinlock_unlock(&page_ht_lock);
 }
 
@@ -197,5 +195,5 @@
  * this call visible.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to wich page belongs.
@@ -206,6 +204,4 @@
 	__native key[2] = { (__address) as, page = ALIGN_DOWN(page, PAGE_SIZE) };
 	
-	spinlock_lock(&page_ht_lock);
-
 	/*
 	 * Note that removed PTE's will be freed
@@ -213,6 +209,4 @@
 	 */
 	hash_table_remove(&page_ht, key, 2);
-
-	spinlock_unlock(&page_ht_lock);
 }
 
@@ -222,5 +216,5 @@
  * Find mapping for virtual page.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to wich page belongs.
@@ -235,11 +229,8 @@
 	__native key[2] = { (__address) as, page = ALIGN_DOWN(page, PAGE_SIZE) };
 	
-	spinlock_lock(&page_ht_lock);
-
 	hlp = hash_table_find(&page_ht, key);
 	if (hlp)
 		t = hash_table_get_instance(hlp, pte_t, link);
 
-	spinlock_unlock(&page_ht_lock);
 	return t;
 }
Index: genarch/src/mm/page_pt.c
===================================================================
--- genarch/src/mm/page_pt.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ genarch/src/mm/page_pt.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -53,5 +53,5 @@
  * using 'flags'.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to wich page belongs.
@@ -106,5 +106,5 @@
  * Empty page tables except PTL0 are freed.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to wich page belongs.
@@ -226,5 +226,5 @@
  * Find mapping for virtual page.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to which page belongs.
Index: generic/include/mm/as.h
===================================================================
--- generic/include/mm/as.h	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ generic/include/mm/as.h	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -94,4 +94,6 @@
 struct as_operations {
 	pte_t *(* page_table_create)(int flags);
+	void (* page_table_lock)(as_t *as, bool lock);
+	void (* page_table_unlock)(as_t *as, bool unlock);
 };
 typedef struct as_operations as_operations_t;
Index: generic/include/mm/page.h
===================================================================
--- generic/include/mm/page.h	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ generic/include/mm/page.h	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -83,4 +83,6 @@
 
 extern void page_init(void);
+extern void page_table_lock(as_t *as, bool lock);
+extern void page_table_unlock(as_t *as, bool unlock);
 extern void page_mapping_insert(as_t *as, __address page, __address frame, int flags);
 extern void page_mapping_remove(as_t *as, __address page);
Index: generic/src/mm/as.c
===================================================================
--- generic/src/mm/as.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ generic/src/mm/as.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -174,5 +174,5 @@
 	
 	ipl = interrupts_disable();
-	spinlock_lock(&as->lock);
+	page_table_lock(as, true);
 	
 	area = find_area_and_lock(as, page);
@@ -184,5 +184,5 @@
 	
 	spinlock_unlock(&area->lock);
-	spinlock_unlock(&as->lock);
+	page_table_unlock(as, true);
 	interrupts_restore(ipl);
 }
@@ -199,10 +199,11 @@
 int as_page_fault(__address page)
 {
+	pte_t *pte;
 	as_area_t *area;
 	__address frame;
 	
 	ASSERT(AS);
+
 	spinlock_lock(&AS->lock);
-	
 	area = find_area_and_lock(AS, page);	
 	if (!area) {
@@ -213,4 +214,20 @@
 		spinlock_unlock(&AS->lock);
 		return 0;
+	}
+
+	page_table_lock(AS, false);
+	
+	/*
+	 * To avoid race condition between two page faults
+	 * on the same address, we need to make sure
+	 * the mapping has not been already inserted.
+	 */
+	if ((pte = page_mapping_find(AS, page))) {
+		if (PTE_PRESENT(pte)) {
+			page_table_unlock(AS, false);
+			spinlock_unlock(&area->lock);
+			spinlock_unlock(&AS->lock);
+			return 1;
+		}
 	}
 
@@ -238,8 +255,8 @@
 	 */
 	page_mapping_insert(AS, page, frame, get_area_flags(area));
+	page_table_unlock(AS, false);
 	
 	spinlock_unlock(&area->lock);
 	spinlock_unlock(&AS->lock);
-
 	return 1;
 }
@@ -358,4 +375,37 @@
 }
 
+/** Lock page table.
+ *
+ * This function should be called before any page_mapping_insert(),
+ * page_mapping_remove() and page_mapping_find().
+ * 
+ * Locking order is such that address space areas must be locked
+ * prior to this call. Address space can be locked prior to this
+ * call in which case the lock argument is false.
+ *
+ * @param as Address space.
+ * @param as_locked If false, do not attempt to lock as->lock.
+ */
+void page_table_lock(as_t *as, bool lock)
+{
+	ASSERT(as_operations);
+	ASSERT(as_operations->page_table_lock);
+
+	as_operations->page_table_lock(as, lock);
+}
+
+/** Unlock page table.
+ *
+ * @param as Address space.
+ * @param as_locked If false, do not attempt to unlock as->lock.
+ */
+void page_table_unlock(as_t *as, bool unlock)
+{
+	ASSERT(as_operations);
+	ASSERT(as_operations->page_table_unlock);
+
+	as_operations->page_table_unlock(as, unlock);
+}
+
 /** Find address space area and change it.
  *
@@ -398,10 +448,18 @@
 			 * Releasing physical memory.
 			 * This depends on the fact that the memory was allocated using frame_alloc().
-			 */ 
+			 */
+			page_table_lock(as, false);
 			pte = page_mapping_find(as, area->base + i*PAGE_SIZE);
 			if (pte && PTE_VALID(pte)) {
+				__address frame;
+
 				ASSERT(PTE_PRESENT(pte));
-				frame_free(ADDR2PFN(PTE_GET_FRAME(pte)));
+				frame = PTE_GET_FRAME(pte);
 				page_mapping_remove(as, area->base + i*PAGE_SIZE);
+				page_table_unlock(as, false);
+
+				frame_free(ADDR2PFN(frame));
+			} else {
+				page_table_unlock(as, false);
 			}
 		}
Index: generic/src/mm/page.c
===================================================================
--- generic/src/mm/page.c	(revision b7dcabb5c2118ec7d9e0680db5929cb212188518)
+++ generic/src/mm/page.c	(revision 2299914109fe20b32a7226821981f5eb11a8394a)
@@ -77,5 +77,5 @@
  * using 'flags'. Allocate and setup any missing page tables.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to wich page belongs.
@@ -98,5 +98,5 @@
  * this call visible.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to wich page belongs.
@@ -115,5 +115,5 @@
  * Find mapping for virtual page.
  *
- * The address space must be locked and interrupts must be disabled.
+ * The page table must be locked and interrupts must be disabled.
  *
  * @param as Address space to wich page belongs.
