diff options
author | Dale Rahn <drahn@cvs.openbsd.org> | 2007-03-05 21:39:23 +0000 |
---|---|---|
committer | Dale Rahn <drahn@cvs.openbsd.org> | 2007-03-05 21:39:23 +0000 |
commit | 866e902b48c30f21638a6d7adf27f0d243ccd646 (patch) | |
tree | f2e33b7433d183cb9cb84463c5e11b4e43470fae /sys/arch/sh | |
parent | 69c92a318e6125e28f03754014a1fe453965a6d4 (diff) |
Work around a virtual aliasing conflict issue. Basically allow multiple
readers but only one writer on a physical page. Not the most optimal, but
has been tested. ok miod@
Diffstat (limited to 'sys/arch/sh')
-rw-r--r-- | sys/arch/sh/sh/pmap.c | 59 |
1 files changed, 52 insertions, 7 deletions
diff --git a/sys/arch/sh/sh/pmap.c b/sys/arch/sh/sh/pmap.c index ed21bf3ca45..a849ff41b0d 100644 --- a/sys/arch/sh/sh/pmap.c +++ b/sys/arch/sh/sh/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.3 2006/11/03 03:38:08 mickey Exp $ */ +/* $OpenBSD: pmap.c,v 1.4 2007/03/05 21:39:22 drahn Exp $ */ /* $NetBSD: pmap.c,v 1.55 2006/08/07 23:19:36 tsutsui Exp $ */ /*- @@ -72,11 +72,12 @@ STATIC struct pool __pmap_pmap_pool; struct pv_entry { struct pmap *pv_pmap; vaddr_t pv_va; + vm_prot_t pv_prot; SLIST_ENTRY(pv_entry) pv_link; }; #define __pmap_pv_alloc() pool_get(&__pmap_pv_pool, PR_NOWAIT) #define __pmap_pv_free(pv) pool_put(&__pmap_pv_pool, (pv)) -STATIC void __pmap_pv_enter(pmap_t, struct vm_page *, vaddr_t); +STATIC void __pmap_pv_enter(pmap_t, struct vm_page *, vaddr_t, vm_prot_t); STATIC void __pmap_pv_remove(pmap_t, struct vm_page *, vaddr_t); STATIC void *__pmap_pv_page_alloc(struct pool *, int); STATIC void __pmap_pv_page_free(struct pool *, void *); @@ -338,7 +339,7 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags) return (0); /* Add to physical-virtual map list of this page */ - __pmap_pv_enter(pmap, pg, va); + __pmap_pv_enter(pmap, pg, va, prot); } else { /* bus-space (always uncached map) */ if (kva) { @@ -433,19 +434,30 @@ __pmap_map_change(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, * Assume pre-existed mapping is already removed. */ void -__pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t va) +__pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t va, vm_prot_t prot) { struct vm_page_md *pvh; struct pv_entry *pv; int s; + int have_writeable = 0; + + if (prot & VM_PROT_WRITE) + have_writeable = 1; s = splvm(); if (SH_HAS_VIRTUAL_ALIAS) { /* Remove all other mapping on this physical page */ pvh = &pg->mdpage; - while ((pv = SLIST_FIRST(&pvh->pvh_head)) != NULL) { - pmap_remove(pv->pv_pmap, pv->pv_va, - pv->pv_va + PAGE_SIZE); + SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) { + if (pv->pv_prot & VM_PROT_WRITE) { + have_writeable = 1; + break; + } + } + if (have_writeable != 0) { + while ((pv = SLIST_FIRST(&pvh->pvh_head)) != NULL) + pmap_remove(pv->pv_pmap, pv->pv_va, + pv->pv_va + PAGE_SIZE); } } @@ -454,6 +466,7 @@ __pmap_pv_enter(pmap_t pmap, struct vm_page *pg, vaddr_t va) pv = __pmap_pv_alloc(); pv->pv_pmap = pmap; pv->pv_va = va; + pv->pv_prot = prot; SLIST_INSERT_HEAD(&pvh->pvh_head, pv, pv_link); splx(s); @@ -606,6 +619,10 @@ pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot) boolean_t kernel = pmap == pmap_kernel(); pt_entry_t *pte, entry; vaddr_t va; + paddr_t pa; + struct vm_page *pg; + struct vm_page_md *pvh; + struct pv_entry *pv, *head; sva = trunc_page(sva); @@ -647,6 +664,34 @@ pmap_protect(pmap_t pmap, vaddr_t sva, vaddr_t eva, vm_prot_t prot) if (pmap->pm_asid != -1) sh_tlb_update(pmap->pm_asid, va, entry); + + if (pmap_extract(pmap, va, &pa)) { + pg = PHYS_TO_VM_PAGE(pa); + if (pg == NULL) + continue; + pvh = &pg->mdpage; + + while ((pv = SLIST_FIRST(&pvh->pvh_head)) != NULL) { + if (pv->pv_pmap == pmap && pv->pv_va == va) { + break; + } + pmap_remove(pv->pv_pmap, pv->pv_va, + pv->pv_va + PAGE_SIZE); + } + /* the matching pv is first in the list */ + SLIST_FOREACH(pv, &pvh->pvh_head, pv_link) { + if (pv->pv_pmap == pmap && pv->pv_va == va) { + pv->pv_prot = prot; + break; + } + } + /* remove the rest of the elements */ + head = SLIST_FIRST(&pvh->pvh_head); + if (head != NULL) + while((pv = SLIST_NEXT(head, pv_link))!= NULL) + pmap_remove(pv->pv_pmap, pv->pv_va, + pv->pv_va + PAGE_SIZE); + } } } |