svn commit: r202894 - in head/sys/i386: i386 include

Alan Cox alc at FreeBSD.org
Sat Jan 23 18:42:29 UTC 2010


Author: alc
Date: Sat Jan 23 18:42:28 2010
New Revision: 202894
URL: http://svn.freebsd.org/changeset/base/202894

Log:
  Handle a race between pmap_kextract() and pmap_promote_pde().  This race is
  known to cause a kernel crash in ZFS on i386 when superpage promotion is
  enabled.
  
  Tested by:	netchild
  MFC after:	1 week

Modified:
  head/sys/i386/i386/pmap.c
  head/sys/i386/include/pmap.h

Modified: head/sys/i386/i386/pmap.c
==============================================================================
--- head/sys/i386/i386/pmap.c	Sat Jan 23 17:58:40 2010	(r202893)
+++ head/sys/i386/i386/pmap.c	Sat Jan 23 18:42:28 2010	(r202894)
@@ -243,8 +243,9 @@ struct sysmaps {
 	caddr_t	CADDR2;
 };
 static struct sysmaps sysmaps_pcpu[MAXCPU];
-pt_entry_t *CMAP1 = 0;
+pt_entry_t *CMAP1 = 0, *KPTmap;
 static pt_entry_t *CMAP3;
+static pd_entry_t *KPTD;
 caddr_t CADDR1 = 0, ptvmmap = 0;
 static caddr_t CADDR3;
 struct msgbuf *msgbufp = 0;
@@ -421,6 +422,21 @@ pmap_bootstrap(vm_paddr_t firstaddr)
 	SYSMAP(struct msgbuf *, unused, msgbufp, atop(round_page(MSGBUF_SIZE)))
 
 	/*
+	 * KPTmap is used by pmap_kextract().
+	 */
+	SYSMAP(pt_entry_t *, KPTD, KPTmap, KVA_PAGES)
+
+	for (i = 0; i < NKPT; i++)
+		KPTD[i] = (KPTphys + (i << PAGE_SHIFT)) | PG_RW | PG_V;
+
+	/*
+	 * Adjust the start of the KPTD and KPTmap so that the implementation
+	 * of pmap_kextract() and pmap_growkernel() can be made simpler.
+	 */
+	KPTD -= KPTDI;
+	KPTmap -= i386_btop(KPTDI << PDRSHIFT);
+
+	/*
 	 * ptemap is used for pmap_pte_quick
 	 */
 	SYSMAP(pt_entry_t *, PMAP1, PADDR1, 1);
@@ -1839,6 +1855,7 @@ pmap_growkernel(vm_offset_t addr)
 	vm_page_t nkpg;
 	pd_entry_t newpdir;
 	pt_entry_t *pde;
+	boolean_t updated_PTD;
 
 	mtx_assert(&kernel_map->system_mtx, MA_OWNED);
 	if (kernel_vm_end == 0) {
@@ -1878,14 +1895,20 @@ pmap_growkernel(vm_offset_t addr)
 			pmap_zero_page(nkpg);
 		ptppaddr = VM_PAGE_TO_PHYS(nkpg);
 		newpdir = (pd_entry_t) (ptppaddr | PG_V | PG_RW | PG_A | PG_M);
-		pdir_pde(PTD, kernel_vm_end) = newpdir;
+		pdir_pde(KPTD, kernel_vm_end) = newpdir;
 
+		updated_PTD = FALSE;
 		mtx_lock_spin(&allpmaps_lock);
 		LIST_FOREACH(pmap, &allpmaps, pm_list) {
+			if ((pmap->pm_pdir[PTDPTDI] & PG_FRAME) == (PTDpde[0] &
+			    PG_FRAME))
+				updated_PTD = TRUE;
 			pde = pmap_pde(pmap, kernel_vm_end);
 			pde_store(pde, newpdir);
 		}
 		mtx_unlock_spin(&allpmaps_lock);
+		KASSERT(updated_PTD,
+		    ("pmap_growkernel: current page table is not in allpmaps"));
 		kernel_vm_end = (kernel_vm_end + PAGE_SIZE * NPTEPG) & ~(PAGE_SIZE * NPTEPG - 1);
 		if (kernel_vm_end - 1 >= kernel_map->max_offset) {
 			kernel_vm_end = kernel_map->max_offset;

Modified: head/sys/i386/include/pmap.h
==============================================================================
--- head/sys/i386/include/pmap.h	Sat Jan 23 17:58:40 2010	(r202893)
+++ head/sys/i386/include/pmap.h	Sat Jan 23 18:42:28 2010	(r202894)
@@ -265,6 +265,16 @@ pte_load_store_ma(pt_entry_t *ptep, pt_e
 #define	pde_store_ma(ptep, pte)	pte_load_store_ma((ptep), (pt_entry_t)pte)
 
 #elif !defined(XEN)
+
+/*
+ * KPTmap is a linear mapping of the kernel page table.  It differs from the
+ * recursive mapping in two ways: (1) it only provides access to kernel page
+ * table pages, and not user page table pages, and (2) it provides access to
+ * a kernel page table page after the corresponding virtual addresses have
+ * been promoted to a 2/4MB page mapping.
+ */
+extern pt_entry_t *KPTmap;
+
 /*
  *	Routine:	pmap_kextract
  *	Function:
@@ -279,10 +289,17 @@ pmap_kextract(vm_offset_t va)
 	if ((pa = PTD[va >> PDRSHIFT]) & PG_PS) {
 		pa = (pa & PG_PS_FRAME) | (va & PDRMASK);
 	} else {
-		pa = *vtopte(va);
+		/*
+		 * Beware of a concurrent promotion that changes the PDE at
+		 * this point!  For example, vtopte() must not be used to
+		 * access the PTE because it would use the new PDE.  It is,
+		 * however, safe to use the old PDE because the page table
+		 * page is preserved by the promotion.
+		 */
+		pa = KPTmap[i386_btop(va)];
 		pa = (pa & PG_FRAME) | (va & PAGE_MASK);
 	}
-	return pa;
+	return (pa);
 }
 
 #define PT_UPDATES_FLUSH()


More information about the svn-src-all mailing list