diff options
author | Visa Hankala <visa@cvs.openbsd.org> | 2018-05-09 14:42:12 +0000 |
---|---|---|
committer | Visa Hankala <visa@cvs.openbsd.org> | 2018-05-09 14:42:12 +0000 |
commit | bf4a4cce6e53c019342907f4953164dd7f3f4231 (patch) | |
tree | ff710545d8d7c461165e61729ec7822d80486c3c /sys/arch | |
parent | e8f3ceb0944b144b6747a2c1e752254d2a98b70e (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.c | 129 |
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; |