aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2023-11-02 18:33:37 +0000
committerMark Johnston <markj@FreeBSD.org>2023-11-02 18:33:37 +0000
commit7703ac2e983bcd44ba93878f52eb30355cbc4821 (patch)
tree05fbc619e9121f10f372f97226984e0514cf6bd8
parent953345922398ae6c029713087c8494a346d90c85 (diff)
downloadsrc-7703ac2e983bcd44ba93878f52eb30355cbc4821.tar.gz
src-7703ac2e983bcd44ba93878f52eb30355cbc4821.zip
riscv: Port improvements from arm64/amd64 pmaps, part 1
- When promoting, do not require that all PTEs all have PTE_A set. Instead, record whether they did and store this information in the PTP's valid bits. - Synchronize some comments in pmap_promote_l2(). - Make pmap_promote_l2() scan starting from the end of the 2MB range instead of the beginning. See the commit log for 9d1b7fa31f510 for justification of this, which I believe applies here as well. Reviewed by: kib MFC after: 1 month Differential Revision: https://reviews.freebsd.org/D42288
-rw-r--r--sys/riscv/include/pte.h2
-rw-r--r--sys/riscv/riscv/pmap.c66
2 files changed, 52 insertions, 16 deletions
diff --git a/sys/riscv/include/pte.h b/sys/riscv/include/pte.h
index 2cede135ebae..da7bd051e122 100644
--- a/sys/riscv/include/pte.h
+++ b/sys/riscv/include/pte.h
@@ -80,7 +80,7 @@ typedef uint64_t pn_t; /* page number */
#define PTE_RWX (PTE_R | PTE_W | PTE_X)
#define PTE_RX (PTE_R | PTE_X)
#define PTE_KERN (PTE_V | PTE_R | PTE_W | PTE_A | PTE_D)
-#define PTE_PROMOTE (PTE_V | PTE_RWX | PTE_D | PTE_A | PTE_G | PTE_U | \
+#define PTE_PROMOTE (PTE_V | PTE_RWX | PTE_D | PTE_G | PTE_U | \
PTE_SW_MANAGED | PTE_SW_WIRED)
/* Bits 63 - 54 are reserved for future use. */
diff --git a/sys/riscv/riscv/pmap.c b/sys/riscv/riscv/pmap.c
index 5747f04b7e0b..30bcf113628c 100644
--- a/sys/riscv/riscv/pmap.c
+++ b/sys/riscv/riscv/pmap.c
@@ -1194,14 +1194,26 @@ pmap_add_delayed_free_list(vm_page_t m, struct spglist *free,
* 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.
+ * If "promoted" is false, then the page table page "mpte" must be zero filled;
+ * "mpte"'s valid field will be set to 0.
+ *
+ * If "promoted" is true and "all_l3e_PTE_A_set" is false, then "mpte" must
+ * contain valid mappings with identical attributes except for PTE_A;
+ * "mpte"'s valid field will be set to 1.
+ *
+ * If "promoted" and "all_l3e_PTE_A_set" are both true, then "mpte" must contain
+ * valid mappings with identical attributes including PTE_A; "mpte"'s valid
+ * field will be set to VM_PAGE_BITS_ALL.
*/
static __inline int
-pmap_insert_pt_page(pmap_t pmap, vm_page_t ml3, bool promoted)
+pmap_insert_pt_page(pmap_t pmap, vm_page_t ml3, bool promoted,
+ bool all_l3e_PTE_A_set)
{
PMAP_LOCK_ASSERT(pmap, MA_OWNED);
- ml3->valid = promoted ? VM_PAGE_BITS_ALL : 0;
+ KASSERT(promoted || !all_l3e_PTE_A_set,
+ ("a zero-filled PTP can't have PTE_A set in every PTE"));
+ ml3->valid = promoted ? (all_l3e_PTE_A_set ? VM_PAGE_BITS_ALL : 1) : 0;
return (vm_radix_insert(&pmap->pm_root, ml3));
}
@@ -2169,7 +2181,7 @@ pmap_remove_kernel_l2(pmap_t pmap, pt_entry_t *l2, vm_offset_t va)
* If this page table page was unmapped by a promotion, then it
* contains valid mappings. Zero it to invalidate those mappings.
*/
- if (ml3->valid != 0)
+ if (vm_page_any_valid(ml3))
pagezero((void *)PHYS_TO_DMAP(ml3pa));
/*
@@ -2230,7 +2242,7 @@ 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,
+ KASSERT(vm_page_any_valid(ml3),
("pmap_remove_l2: l3 page not promoted"));
pmap_resident_count_dec(pmap, 1);
KASSERT(ml3->ref_count == Ln_ENTRIES,
@@ -2703,7 +2715,7 @@ pmap_demote_l2_locked(pmap_t pmap, pd_entry_t *l2, vm_offset_t va,
* If the page table page is not leftover from an earlier promotion,
* initialize it.
*/
- if (mpte->valid == 0) {
+ if (!vm_page_all_valid(mpte)) {
for (i = 0; i < Ln_ENTRIES; i++)
pmap_store(firstl3 + i, newl3 + (i << PTE_PPN0_S));
}
@@ -2712,8 +2724,7 @@ pmap_demote_l2_locked(pmap_t pmap, pd_entry_t *l2, vm_offset_t va,
"addresses"));
/*
- * If the mapping has changed attributes, update the page table
- * entries.
+ * If the mapping has changed attributes, update the PTEs.
*/
if ((pmap_load(firstl3) & PTE_PROMOTE) != (newl3 & PTE_PROMOTE))
for (i = 0; i < Ln_ENTRIES; i++)
@@ -2752,7 +2763,7 @@ static void
pmap_promote_l2(pmap_t pmap, pd_entry_t *l2, vm_offset_t va, vm_page_t ml3,
struct rwlock **lockp)
{
- pt_entry_t *firstl3, firstl3e, *l3, l3e;
+ pt_entry_t all_l3e_PTE_A, *firstl3, firstl3e, *l3, l3e;
vm_paddr_t pa;
PMAP_LOCK_ASSERT(pmap, MA_OWNED);
@@ -2760,6 +2771,11 @@ pmap_promote_l2(pmap_t pmap, pd_entry_t *l2, vm_offset_t va, vm_page_t ml3,
KASSERT((pmap_load(l2) & PTE_RWX) == 0,
("pmap_promote_l2: invalid l2 entry %p", l2));
+ /*
+ * Examine the first L3E in the specified PTP. Abort if this L3E is
+ * ineligible for promotion or does not map the first 4KB physical page
+ * within a 2MB page.
+ */
firstl3 = (pt_entry_t *)PHYS_TO_DMAP(PTE_TO_PHYS(pmap_load(l2)));
firstl3e = pmap_load(firstl3);
pa = PTE_TO_PHYS(firstl3e);
@@ -2787,8 +2803,14 @@ pmap_promote_l2(pmap_t pmap, pd_entry_t *l2, vm_offset_t va, vm_page_t ml3,
}
}
- pa += PAGE_SIZE;
- for (l3 = firstl3 + 1; l3 < firstl3 + Ln_ENTRIES; l3++) {
+ /*
+ * Examine each of the other PTEs in the specified PTP. Abort if this
+ * PTE maps an unexpected 4KB physical page or does not have identical
+ * characteristics to the first PTE.
+ */
+ all_l3e_PTE_A = firstl3e & PTE_A;
+ pa += L2_SIZE - PAGE_SIZE;
+ for (l3 = firstl3 + Ln_ENTRIES - 1; l3 > firstl3; l3--) {
l3e = pmap_load(l3);
if (PTE_TO_PHYS(l3e) != pa) {
CTR2(KTR_PMAP,
@@ -2810,14 +2832,28 @@ pmap_promote_l2(pmap_t pmap, pd_entry_t *l2, vm_offset_t va, vm_page_t ml3,
atomic_add_long(&pmap_l2_p_failures, 1);
return;
}
- pa += PAGE_SIZE;
+ all_l3e_PTE_A &= l3e;
+ pa -= PAGE_SIZE;
}
+ /*
+ * Unless all PTEs have PTE_A set, clear it from the superpage
+ * mapping, so that promotions triggered by speculative mappings,
+ * such as pmap_enter_quick(), don't automatically mark the
+ * underlying pages as referenced.
+ */
+ firstl3e &= ~PTE_A | all_l3e_PTE_A;
+
+ /*
+ * Save the page table page in its current state until the L2
+ * mapping the superpage is demoted by pmap_demote_l2() or
+ * destroyed by pmap_remove_l3().
+ */
if (ml3 == NULL)
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, true)) {
+ if (pmap_insert_pt_page(pmap, ml3, true, all_l3e_PTE_A != 0)) {
CTR2(KTR_PMAP, "pmap_promote_l2: failure for va %#lx pmap %p",
va, pmap);
atomic_add_long(&pmap_l2_p_failures, 1);
@@ -3229,7 +3265,7 @@ pmap_enter_l2(pmap_t pmap, vm_offset_t va, pd_entry_t new_l2, u_int flags,
* 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, false))
+ if (pmap_insert_pt_page(pmap, mt, false, false))
panic("pmap_enter_l2: trie insert failed");
} else
KASSERT(pmap_load(l2) == 0,
@@ -3847,7 +3883,7 @@ 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(mpte->valid == VM_PAGE_BITS_ALL,
+ KASSERT(vm_page_any_valid(mpte),
("pmap_remove_pages: pte page not promoted"));
pmap_resident_count_dec(pmap, 1);
KASSERT(mpte->ref_count == Ln_ENTRIES,