git: 7a5ef20a977c - main - riscv/pmap.c: Handle spurious kernel page faults in critical sections
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 10 Jul 2026 16:03:46 UTC
The branch main has been updated by bnovkov:
URL: https://cgit.FreeBSD.org/src/commit/?id=7a5ef20a977c2d1ae5b94e6e6e927ac7a566ff10
commit 7a5ef20a977c2d1ae5b94e6e6e927ac7a566ff10
Author: Bojan Novković <bnovkov@FreeBSD.org>
AuthorDate: 2026-06-10 19:13:03 +0000
Commit: Bojan Novković <bnovkov@FreeBSD.org>
CommitDate: 2026-07-10 15:56:57 +0000
riscv/pmap.c: Handle spurious kernel page faults in critical sections
The Privileged ISA specification permits caching of invalid PTEs
12.2.1. Supervisor Memory-Management Fence Instruction), which may
result in a spurious page fault. Such faults are handled by 'pmap_fault'
which locks the kernel pmap before inspecting and possibly updating the
offending L2 entry.
Unfortunately, spurious faults may also occur when we're already holding
the kernel_pmap lock or running in a critical section, where any attempt
to grab the pmap lock will result in a kernel panic.
Fix this avoidable panic by performing a lockless lookup to determine
whether a valid kernel mapping exits and flushing appropriate TLB entry.
Differential Revision: https://reviews.freebsd.org/D56925
Reviewed by: jrtc27, mhorne, markj
---
sys/riscv/riscv/pmap.c | 46 +++++++++++++++++++++++++++++++++++-----------
1 file changed, 35 insertions(+), 11 deletions(-)
diff --git a/sys/riscv/riscv/pmap.c b/sys/riscv/riscv/pmap.c
index 35194ebd7c28..67123f9bfcc8 100644
--- a/sys/riscv/riscv/pmap.c
+++ b/sys/riscv/riscv/pmap.c
@@ -2911,28 +2911,52 @@ retryl3:
PMAP_UNLOCK(pmap);
}
+static pt_entry_t *
+pmap_fault_lookup(pmap_t pmap, vm_offset_t va)
+{
+ pd_entry_t *l2, l2e;
+
+ l2 = pmap_l2(pmap, va);
+ if (l2 == NULL || ((l2e = pmap_load(l2)) & PTE_V) == 0)
+ return (NULL);
+ if ((l2e & PTE_RWX) == 0)
+ return (pmap_l2_to_l3(l2, va));
+ return (l2);
+}
+
int
pmap_fault(pmap_t pmap, vm_offset_t va, vm_prot_t ftype)
{
- pd_entry_t *l2, l2e;
pt_entry_t bits, *pte, oldpte;
int rv;
KASSERT(VIRT_IS_VALID(va), ("pmap_fault: invalid va %#lx", va));
+ if (pmap == kernel_pmap) {
+ /*
+ * Locking the kernel pmap while processing spurious faults
+ * may lead to a panic since we might be running a critical section
+ * or already holding the kernel pmap lock.
+ * We deal with this by taking advantage of the fact that
+ * kernel PTPs are never freed and performing a lockless lookup
+ * to determine whether a valid mapping exits.
+ */
+ pte = pmap_fault_lookup(pmap, va);
+ if (pte != NULL && (pmap_load(pte) & PTE_KERN) == PTE_KERN) {
+ sfence_vma_page(va);
+ return (1);
+ }
+ /*
+ * The entry is either not present or missing some bits.
+ * Fall back to the locked lookup below to handle the fault.
+ */
+ }
+
rv = 0;
PMAP_LOCK(pmap);
- l2 = pmap_l2(pmap, va);
- if (l2 == NULL || ((l2e = pmap_load(l2)) & PTE_V) == 0)
+ pte = pmap_fault_lookup(pmap, va);
+ if (pte == NULL || ((oldpte = pmap_load(pte)) & PTE_V) == 0)
goto done;
- if ((l2e & PTE_RWX) == 0) {
- pte = pmap_l2_to_l3(l2, va);
- if (((oldpte = pmap_load(pte)) & PTE_V) == 0)
- goto done;
- } else {
- pte = l2;
- oldpte = l2e;
- }
if ((pmap != kernel_pmap && (oldpte & PTE_U) == 0) ||
(ftype == VM_PROT_WRITE && (oldpte & PTE_W) == 0) ||