aboutsummaryrefslogtreecommitdiff
path: root/sys/riscv
diff options
context:
space:
mode:
authorAlan Cox <alc@FreeBSD.org>2019-06-09 03:36:10 +0000
committerAlan Cox <alc@FreeBSD.org>2019-06-09 03:36:10 +0000
commitfd2dae0a30e937717faf87f626630a8a5b8f724a (patch)
tree3dcafe06c799730072830cf905934f1ef7adfc06 /sys/riscv
parentc3308a946999087e94335c85a01838e4e83b408f (diff)
downloadsrc-fd2dae0a30e937717faf87f626630a8a5b8f724a.tar.gz
src-fd2dae0a30e937717faf87f626630a8a5b8f724a.zip
Implement an alternative solution to the amd64 and i386 pmap problem that we
previously addressed in r348246. This pmap problem also exists on arm64 and riscv. However, the original solution developed for amd64 and i386 cannot be used on arm64 and riscv. In particular, arm64 and riscv do not define a PG_PROMOTED flag in their level 2 PTEs. (A PG_PROMOTED flag makes no sense on arm64, where unlike x86 or riscv we are required to break the old 4KB mappings before making the 2MB mapping; and on riscv there are no unused bits in the PTE to define a PG_PROMOTED flag.) This commit implements an alternative solution that can be used on all four architectures. Moreover, this solution has two other advantages. First, on older AMD processors that required the Erratum 383 workaround, it is less costly. Specifically, it avoids unnecessary calls to pmap_fill_ptp() on a superpage demotion. Second, it enables the elimination of some calls to pagezero() in pmap_kernel_remove_{l2,pde}(). In addition, remove a related stale comment from pmap_enter_{l2,pde}(). Reviewed by: kib, markj (an earlier version) MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D20538
Notes
Notes: svn path=/head/; revision=348828
Diffstat (limited to 'sys/riscv')
-rw-r--r--sys/riscv/riscv/pmap.c39
1 files changed, 24 insertions, 15 deletions
diff --git a/sys/riscv/riscv/pmap.c b/sys/riscv/riscv/pmap.c
index 0a6819f23bf6..9df759083163 100644
--- a/sys/riscv/riscv/pmap.c
+++ b/sys/riscv/riscv/pmap.c
@@ -1104,12 +1104,15 @@ pmap_add_delayed_free_list(vm_page_t m, struct spglist *free,
* of idle page table pages. Each of a pmap's page table pages is responsible
* for mapping a distinct range of virtual addresses. The pmap's collection is
* ordered by this virtual address range.
+ *
+ * If "promoted" is false, then the page table page "ml3" must be zero filled.
*/
static __inline int
-pmap_insert_pt_page(pmap_t pmap, vm_page_t ml3)
+pmap_insert_pt_page(pmap_t pmap, vm_page_t ml3, bool promoted)
{
PMAP_LOCK_ASSERT(pmap, MA_OWNED);
+ ml3->valid = promoted ? VM_PAGE_BITS_ALL : 0;
return (vm_radix_insert(&pmap->pm_root, ml3));
}
@@ -2002,9 +2005,11 @@ pmap_remove_kernel_l2(pmap_t pmap, pt_entry_t *l2, vm_offset_t va)
newl2 = ml3pa | PTE_V;
/*
- * Initialize the page table page.
+ * If this page table page was unmapped by a promotion, then it
+ * contains valid mappings. Zero it to invalidate those mappings.
*/
- pagezero((void *)PHYS_TO_DMAP(ml3pa));
+ if (ml3->valid != 0)
+ pagezero((void *)PHYS_TO_DMAP(ml3pa));
/*
* Demote the mapping.
@@ -2064,6 +2069,8 @@ pmap_remove_l2(pmap_t pmap, pt_entry_t *l2, vm_offset_t sva,
} else {
ml3 = pmap_remove_pt_page(pmap, sva);
if (ml3 != NULL) {
+ KASSERT(ml3->valid == VM_PAGE_BITS_ALL,
+ ("pmap_remove_l2: l3 page not promoted"));
pmap_resident_count_dec(pmap, 1);
KASSERT(ml3->wire_count == Ln_ENTRIES,
("pmap_remove_l2: l3 page wire count error"));
@@ -2482,8 +2489,10 @@ pmap_demote_l2_locked(pmap_t pmap, pd_entry_t *l2, vm_offset_t va,
"failure for va %#lx in pmap %p", va, pmap);
return (false);
}
- if (va < VM_MAXUSER_ADDRESS)
+ if (va < VM_MAXUSER_ADDRESS) {
+ mpte->wire_count = Ln_ENTRIES;
pmap_resident_count_inc(pmap, 1);
+ }
}
mptepa = VM_PAGE_TO_PHYS(mpte);
firstl3 = (pt_entry_t *)PHYS_TO_DMAP(mptepa);
@@ -2495,10 +2504,10 @@ pmap_demote_l2_locked(pmap_t pmap, pd_entry_t *l2, vm_offset_t va,
newl3 = oldl2;
/*
- * 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 = Ln_ENTRIES;
+ if (mpte->valid == 0) {
for (i = 0; i < Ln_ENTRIES; i++)
pmap_store(firstl3 + i, newl3 + (i << PTE_PPN0_S));
}
@@ -2589,7 +2598,7 @@ pmap_promote_l2(pmap_t pmap, pd_entry_t *l2, vm_offset_t va,
ml3 = PHYS_TO_VM_PAGE(PTE_TO_PHYS(pmap_load(l2)));
KASSERT(ml3->pindex == pmap_l2_pindex(va),
("pmap_promote_l2: page table page's pindex is wrong"));
- if (pmap_insert_pt_page(pmap, ml3)) {
+ if (pmap_insert_pt_page(pmap, ml3, true)) {
CTR2(KTR_PMAP, "pmap_promote_l2: failure for va %#lx pmap %p",
va, pmap);
atomic_add_long(&pmap_l2_p_failures, 1);
@@ -2972,15 +2981,13 @@ pmap_enter_l2(pmap_t pmap, vm_offset_t va, pd_entry_t new_l2, u_int flags,
}
vm_page_free_pages_toq(&free, true);
if (va >= VM_MAXUSER_ADDRESS) {
+ /*
+ * Both pmap_remove_l2() and pmap_remove_l3() will
+ * leave the kernel page table page zero filled.
+ */
mt = PHYS_TO_VM_PAGE(PTE_TO_PHYS(pmap_load(l2)));
- if (pmap_insert_pt_page(pmap, mt)) {
- /*
- * XXX Currently, this can't happen bacuse
- * we do not perform pmap_enter(psind == 1)
- * on the kernel pmap.
- */
+ if (pmap_insert_pt_page(pmap, mt, false))
panic("pmap_enter_l2: trie insert failed");
- }
} else
KASSERT(pmap_load(l2) == 0,
("pmap_enter_l2: non-zero L2 entry %p", l2));
@@ -3557,6 +3564,8 @@ pmap_remove_pages_pv(pmap_t pmap, vm_page_t m, pv_entry_t pv,
}
mpte = pmap_remove_pt_page(pmap, pv->pv_va);
if (mpte != NULL) {
+ KASSERT(ml3->valid == VM_PAGE_BITS_ALL,
+ ("pmap_remove_pages: l3 page not promoted"));
pmap_resident_count_dec(pmap, 1);
KASSERT(mpte->wire_count == Ln_ENTRIES,
("pmap_remove_pages: pte page wire count error"));