summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorVisa Hankala <visa@cvs.openbsd.org>2018-05-09 14:42:12 +0000
committerVisa Hankala <visa@cvs.openbsd.org>2018-05-09 14:42:12 +0000
commitbf4a4cce6e53c019342907f4953164dd7f3f4231 (patch)
treeff710545d8d7c461165e61729ec7822d80486c3c /sys/arch
parente8f3ceb0944b144b6747a2c1e752254d2a98b70e (diff)
Avoid locking two vm_pages simultaneously in pmap_enter(). To achieve
that, remove any existing mapping before locking the new vm_page. This fix prevents a deadlock. Issue pointed out by witness(4).
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/mips64/mips64/pmap.c129
1 files changed, 58 insertions, 71 deletions
diff --git a/sys/arch/mips64/mips64/pmap.c b/sys/arch/mips64/mips64/pmap.c
index 62d9fdec983..48ae996fde2 100644
--- a/sys/arch/mips64/mips64/pmap.c
+++ b/sys/arch/mips64/mips64/pmap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pmap.c,v 1.109 2018/05/09 14:28:36 visa Exp $ */
+/* $OpenBSD: pmap.c,v 1.110 2018/05/09 14:42:11 visa Exp $ */
/*
* Copyright (c) 2001-2004 Opsycon AB (www.opsycon.se / www.opsycon.com)
@@ -1034,18 +1034,64 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
pmap_lock(pmap);
-#ifdef DIAGNOSTIC
+ /*
+ * Get the PTE. Allocate page directory and page table pages
+ * if necessary.
+ */
if (pmap == pmap_kernel()) {
- stat_count(enter_stats.kernel);
+#ifdef DIAGNOSTIC
if (va < VM_MIN_KERNEL_ADDRESS ||
va >= VM_MAX_KERNEL_ADDRESS)
- panic("pmap_enter: kva %p", (void *)va);
+ panic("%s: kva %p", __func__, (void *)va);
+#endif
+ stat_count(enter_stats.kernel);
+ pte = kvtopte(va);
} else {
- stat_count(enter_stats.user);
+#ifdef DIAGNOSTIC
if (va >= VM_MAXUSER_ADDRESS)
- panic("pmap_enter: uva %p", (void *)va);
- }
+ panic("%s: uva %p", __func__, (void *)va);
#endif
+ stat_count(enter_stats.user);
+ if ((pde = pmap_segmap(pmap, va)) == NULL) {
+ pde = pool_get(&pmap_pg_pool, PR_NOWAIT | PR_ZERO);
+ if (pde == NULL) {
+ if (flags & PMAP_CANFAIL) {
+ pmap_unlock(pmap);
+ return ENOMEM;
+ }
+ panic("%s: out of memory", __func__);
+ }
+ pmap_segmap(pmap, va) = pde;
+ }
+ if ((pte = pde[uvtopde(va)]) == NULL) {
+ pte = pool_get(&pmap_pg_pool, PR_NOWAIT | PR_ZERO);
+ if (pte == NULL) {
+ if (flags & PMAP_CANFAIL) {
+ pmap_unlock(pmap);
+ return ENOMEM;
+ }
+ panic("%s: out of memory", __func__);
+ }
+ pde[uvtopde(va)] = pte;
+ }
+ pte += uvtopte(va);
+ }
+
+ if ((*pte & PG_V) && pa != pfn_to_pad(*pte)) {
+ pmap_do_remove(pmap, va, va + PAGE_SIZE);
+ stat_count(enter_stats.mchange);
+ }
+
+ if ((*pte & PG_V) == 0) {
+ atomic_inc_long(&pmap->pm_stats.resident_count);
+ if (wired)
+ atomic_inc_long(&pmap->pm_stats.wired_count);
+ } else {
+ if ((*pte & PG_WIRED) != 0 && wired == 0)
+ atomic_dec_long(&pmap->pm_stats.wired_count);
+ else if ((*pte & PG_WIRED) == 0 && wired != 0)
+ atomic_inc_long(&pmap->pm_stats.wired_count);
+ }
if (pg != NULL) {
mtx_enter(&pg->mdpage.pv_mtx);
@@ -1097,6 +1143,10 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
npte |= pg_xi;
if (pmap == pmap_kernel()) {
+ /*
+ * Enter kernel space mapping.
+ */
+
if (pg != NULL) {
if (pmap_enter_pv(pmap, va, pg, &npte) != 0) {
if (flags & PMAP_CANFAIL) {
@@ -1108,21 +1158,6 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
}
}
- pte = kvtopte(va);
- if ((*pte & PG_V) && pa != pfn_to_pad(*pte)) {
- pmap_do_remove(pmap, va, va + PAGE_SIZE);
- stat_count(enter_stats.mchange);
- }
- if ((*pte & PG_V) == 0) {
- atomic_inc_long(&pmap->pm_stats.resident_count);
- if (wired)
- atomic_inc_long(&pmap->pm_stats.wired_count);
- } else {
- if ((*pte & PG_WIRED) != 0 && wired == 0)
- atomic_dec_long(&pmap->pm_stats.wired_count);
- else if ((*pte & PG_WIRED) == 0 && wired != 0)
- atomic_inc_long(&pmap->pm_stats.wired_count);
- }
npte |= vad_to_pfn(pa) | PG_G;
if (wired)
npte |= PG_WIRED;
@@ -1142,34 +1177,8 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
}
/*
- * User space mapping. Do table build.
+ * Enter user space mapping.
*/
- if ((pde = pmap_segmap(pmap, va)) == NULL) {
- pde = pool_get(&pmap_pg_pool, PR_NOWAIT | PR_ZERO);
- if (pde == NULL) {
- if (flags & PMAP_CANFAIL) {
- if (pg != NULL)
- mtx_leave(&pg->mdpage.pv_mtx);
- pmap_unlock(pmap);
- return ENOMEM;
- }
- panic("%s: out of memory", __func__);
- }
- pmap_segmap(pmap, va) = pde;
- }
- if ((pte = pde[uvtopde(va)]) == NULL) {
- pte = pool_get(&pmap_pg_pool, PR_NOWAIT | PR_ZERO);
- if (pte == NULL) {
- if (flags & PMAP_CANFAIL) {
- if (pg != NULL)
- mtx_leave(&pg->mdpage.pv_mtx);
- pmap_unlock(pmap);
- return ENOMEM;
- }
- panic("%s: out of memory", __func__);
- }
- pde[uvtopde(va)] = pte;
- }
if (pg != NULL) {
if (pmap_enter_pv(pmap, va, pg, &npte) != 0) {
@@ -1182,28 +1191,6 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
}
}
- pte += uvtopte(va);
-
- /*
- * Now validate mapping with desired protection/wiring.
- * Assume uniform modified and referenced status for all
- * MIPS pages in a OpenBSD page.
- */
- if ((*pte & PG_V) && pa != pfn_to_pad(*pte)) {
- pmap_do_remove(pmap, va, va + PAGE_SIZE);
- stat_count(enter_stats.mchange);
- }
- if ((*pte & PG_V) == 0) {
- atomic_inc_long(&pmap->pm_stats.resident_count);
- if (wired)
- atomic_inc_long(&pmap->pm_stats.wired_count);
- } else {
- if ((*pte & PG_WIRED) != 0 && wired == 0)
- atomic_dec_long(&pmap->pm_stats.wired_count);
- else if ((*pte & PG_WIRED) == 0 && wired != 0)
- atomic_inc_long(&pmap->pm_stats.wired_count);
- }
-
npte |= vad_to_pfn(pa);
if (wired)
npte |= PG_WIRED;