svn commit: r348246 - in head/sys: amd64/amd64 i386/i386

Konstantin Belousov kib at FreeBSD.org
Fri May 24 17:19:08 UTC 2019


Author: kib
Date: Fri May 24 17:19:06 2019
New Revision: 348246
URL: https://svnweb.freebsd.org/changeset/base/348246

Log:
  Fix a corner case in demotion of kernel mappings.
  
  It is possible for the kernel mapping to be created with superpage by
  directly installing pde using pmap_enter_2mpage() without filling the
  corresponding page table page.  This can happen e.g. if the range is
  already backed by reservation and vm_fault_soft_fast() conditions are
  satisfied, which was observed on the pipe_map.
  
  In this case, demotion must fill the page obtained from the pmap
  radix, same as if the page is newly allocated.  Use PG_PROMOTED bit as
  an indicator that the page is valid, instead of the wire count of the
  page table page.
  
  Since the PG_PROMOTED bit is set on pde when we leave TLB entries for
  4k pages around, which in particular means that the ptes were filled,
  it provides more correct indicator.  Note that pmap_protect_pde()
  clears PG_PROMOTED, which handles the case when protection was changed
  on the superpage without adjusting ptes.
  
  Reported by:	pho
  In collaboration with:	alc
  Tested by:	alc, pho
  Sponsored by:	The FreeBSD Foundation
  MFC after:	1 week
  Differential revision:	https://reviews.freebsd.org/D20380

Modified:
  head/sys/amd64/amd64/pmap.c
  head/sys/i386/i386/pmap.c

Modified: head/sys/amd64/amd64/pmap.c
==============================================================================
--- head/sys/amd64/amd64/pmap.c	Fri May 24 17:14:07 2019	(r348245)
+++ head/sys/amd64/amd64/pmap.c	Fri May 24 17:19:06 2019	(r348246)
@@ -4537,8 +4537,10 @@ pmap_demote_pde_locked(pmap_t pmap, pd_entry_t *pde, v
 			    " in pmap %p", va, pmap);
 			return (FALSE);
 		}
-		if (va < VM_MAXUSER_ADDRESS)
+		if (va < VM_MAXUSER_ADDRESS) {
+			mpte->wire_count = NPTEPG;
 			pmap_resident_count_inc(pmap, 1);
+		}
 	}
 	mptepa = VM_PAGE_TO_PHYS(mpte);
 	firstpte = (pt_entry_t *)PHYS_TO_DMAP(mptepa);
@@ -4551,12 +4553,12 @@ pmap_demote_pde_locked(pmap_t pmap, pd_entry_t *pde, v
 	newpte = pmap_swap_pat(pmap, newpte);
 
 	/*
-	 * If the page table page is new, initialize it.
+	 * If the page table page is not leftover from an earlier promotion,
+	 * initialize it.
 	 */
-	if (mpte->wire_count == 1) {
-		mpte->wire_count = NPTEPG;
+	if ((oldpde & PG_PROMOTED) == 0)
 		pmap_fill_ptp(firstpte, newpte);
-	}
+
 	KASSERT((*firstpte & PG_FRAME) == (newpte & PG_FRAME),
 	    ("pmap_demote_pde: firstpte and newpte map different physical"
 	    " addresses"));

Modified: head/sys/i386/i386/pmap.c
==============================================================================
--- head/sys/i386/i386/pmap.c	Fri May 24 17:14:07 2019	(r348245)
+++ head/sys/i386/i386/pmap.c	Fri May 24 17:19:06 2019	(r348246)
@@ -2768,8 +2768,10 @@ pmap_demote_pde(pmap_t pmap, pd_entry_t *pde, vm_offse
 			    " in pmap %p", va, pmap);
 			return (FALSE);
 		}
-		if (pmap != kernel_pmap)
+		if (pmap != kernel_pmap) {
+			mpte->wire_count = NPTEPG;
 			pmap->pm_stats.resident_count++;
+		}
 	}
 	mptepa = VM_PAGE_TO_PHYS(mpte);
 
@@ -2818,12 +2820,12 @@ pmap_demote_pde(pmap_t pmap, pd_entry_t *pde, vm_offse
 		newpte ^= PG_PDE_PAT | PG_PTE_PAT;
 
 	/*
-	 * If the page table page is new, initialize it.
+	 * If the page table page is not leftover from an earlier promotion,
+	 * initialize it.
 	 */
-	if (mpte->wire_count == 1) {
-		mpte->wire_count = NPTEPG;
+	if ((oldpde & PG_PROMOTED) == 0)
 		pmap_fill_ptp(firstpte, newpte);
-	}
+
 	KASSERT((*firstpte & PG_FRAME) == (newpte & PG_FRAME),
 	    ("pmap_demote_pde: firstpte and newpte map different physical"
 	    " addresses"));


More information about the svn-src-head mailing list