summaryrefslogtreecommitdiff
path: root/sys/arch/sh
diff options
context:
space:
mode:
authorDale Rahn <drahn@cvs.openbsd.org>2007-03-05 21:39:23 +0000
committerDale Rahn <drahn@cvs.openbsd.org>2007-03-05 21:39:23 +0000
commit866e902b48c30f21638a6d7adf27f0d243ccd646 (patch)
treef2e33b7433d183cb9cb84463c5e11b4e43470fae /sys/arch/sh
parent69c92a318e6125e28f03754014a1fe453965a6d4 (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.c59
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);
+ }
}
}