Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 16 Jun 2019 16:45:01 +0000 (UTC)
From:      Alan Cox <alc@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r349117 - head/sys/arm64/arm64
Message-ID:  <201906161645.x5GGj12A081585@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: alc
Date: Sun Jun 16 16:45:01 2019
New Revision: 349117
URL: https://svnweb.freebsd.org/changeset/base/349117

Log:
  Three enhancements to arm64's pmap_protect():
  
  Implement protection changes on superpage mappings.  Previously, a superpage
  mapping was unconditionally demoted by pmap_protect(), even if the
  protection change applied to the entire superpage mapping.
  
  Precompute the bit mask describing the protection changes rather than
  recomputing it for every page table entry that is changed.
  
  Skip page table entries that already have the requested protection changes
  in place.
  
  Reviewed by:	andrew, kib
  MFC after:	10 days
  Differential Revision:	https://reviews.freebsd.org/D20657

Modified:
  head/sys/arm64/arm64/pmap.c

Modified: head/sys/arm64/arm64/pmap.c
==============================================================================
--- head/sys/arm64/arm64/pmap.c	Sun Jun 16 16:02:50 2019	(r349116)
+++ head/sys/arm64/arm64/pmap.c	Sun Jun 16 16:45:01 2019	(r349117)
@@ -2729,6 +2729,51 @@ retry:
 }
 
 /*
+ * pmap_protect_l2: do the things to protect a 2MB page in a pmap
+ */
+static void
+pmap_protect_l2(pmap_t pmap, pt_entry_t *l2, vm_offset_t sva, pt_entry_t nbits)
+{
+	pd_entry_t old_l2;
+	vm_page_t m, mt;
+
+	PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+	KASSERT((sva & L2_OFFSET) == 0,
+	    ("pmap_protect_l2: sva is not 2mpage aligned"));
+	old_l2 = pmap_load(l2);
+	KASSERT((old_l2 & ATTR_DESCR_MASK) == L2_BLOCK,
+	    ("pmap_protect_l2: L2e %lx is not a block mapping", old_l2));
+
+	/*
+	 * Return if the L2 entry already has the desired access restrictions
+	 * in place.
+	 */
+	if ((old_l2 | nbits) == old_l2)
+		return;
+
+	/*
+	 * When a dirty read/write superpage mapping is write protected,
+	 * update the dirty field of each of the superpage's constituent 4KB
+	 * pages.
+	 */
+	if ((nbits & ATTR_AP(ATTR_AP_RO)) != 0 &&
+	    (old_l2 & ATTR_SW_MANAGED) != 0 &&
+	    pmap_page_dirty(old_l2)) {
+		m = PHYS_TO_VM_PAGE(old_l2 & ~ATTR_MASK);
+		for (mt = m; mt < &m[L2_SIZE / PAGE_SIZE]; mt++)
+			vm_page_dirty(mt);
+	}
+
+	pmap_set(l2, nbits);
+
+	/*
+	 * Since a promotion must break the 4KB page mappings before making
+	 * the 2MB page mapping, a pmap_invalidate_page() suffices.
+	 */
+	pmap_invalidate_page(pmap, sva);
+}
+
+/*
  *	Set the physical protection on the
  *	specified range of this map as requested.
  */
@@ -2745,8 +2790,12 @@ pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t
 		return;
 	}
 
-	if ((prot & (VM_PROT_WRITE | VM_PROT_EXECUTE)) ==
-	    (VM_PROT_WRITE | VM_PROT_EXECUTE))
+	nbits = 0;
+	if ((prot & VM_PROT_WRITE) == 0)
+		nbits |= ATTR_AP(ATTR_AP_RO);
+	if ((prot & VM_PROT_EXECUTE) == 0)
+		nbits |= ATTR_XN;
+	if (nbits == 0)
 		return;
 
 	PMAP_LOCK(pmap);
@@ -2777,9 +2826,11 @@ pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t
 			continue;
 
 		if ((pmap_load(l2) & ATTR_DESCR_MASK) == L2_BLOCK) {
-			l3p = pmap_demote_l2(pmap, l2, sva);
-			if (l3p == NULL)
+			if (sva + L2_SIZE == va_next && eva >= va_next) {
+				pmap_protect_l2(pmap, l2, sva, nbits);
 				continue;
+			} else if (pmap_demote_l2(pmap, l2, sva) == NULL)
+				continue;
 		}
 		KASSERT((pmap_load(l2) & ATTR_DESCR_MASK) == L2_TABLE,
 		    ("pmap_protect: Invalid L2 entry after demotion"));
@@ -2790,8 +2841,16 @@ pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t
 		va = va_next;
 		for (l3p = pmap_l2_to_l3(l2, sva); sva != va_next; l3p++,
 		    sva += L3_SIZE) {
+			/*
+			 * Go to the next L3 entry if the current one is
+			 * invalid or already has the desired access
+			 * restrictions in place.  (The latter case occurs
+			 * frequently.  For example, in a "buildworld"
+			 * workload, almost 1 out of 4 L3 entries already
+			 * have the desired restrictions.)
+			 */
 			l3 = pmap_load(l3p);
-			if (!pmap_l3_valid(l3)) {
+			if (!pmap_l3_valid(l3) || (l3 | nbits) == l3) {
 				if (va != va_next) {
 					pmap_invalidate_range(pmap, va, sva);
 					va = va_next;
@@ -2801,17 +2860,14 @@ pmap_protect(pmap_t pmap, vm_offset_t sva, vm_offset_t
 			if (va == va_next)
 				va = sva;
 
-			nbits = 0;
-			if ((prot & VM_PROT_WRITE) == 0) {
-				if ((l3 & ATTR_SW_MANAGED) &&
-				    pmap_page_dirty(l3)) {
-					vm_page_dirty(PHYS_TO_VM_PAGE(l3 &
-					    ~ATTR_MASK));
-				}
-				nbits |= ATTR_AP(ATTR_AP_RO);
-			}
-			if ((prot & VM_PROT_EXECUTE) == 0)
-				nbits |= ATTR_XN;
+			/*
+			 * When a dirty read/write mapping is write protected,
+			 * update the page's dirty field.
+			 */
+			if ((nbits & ATTR_AP(ATTR_AP_RO)) != 0 &&
+			    (l3 & ATTR_SW_MANAGED) != 0 &&
+			    pmap_page_dirty(l3))
+				vm_page_dirty(PHYS_TO_VM_PAGE(l3 & ~ATTR_MASK));
 
 			pmap_set(l3p, nbits);
 		}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906161645.x5GGj12A081585>