- Make lazy I-sync a little safer -- always mark the pmap as needing

I-sync in pmap_remove_mapping() if the old mapping had PG_EXEC, and
  kick curcpu (IMB) or other CPUs (via an IPI) only if the pmap was
  the kernel pmap or active on other CPUs (curcpu is handled in userret()).
- Use lazy I-sync everywhere, (hopefully) eliminating the last of the
  I-sync issues for multiprocessor support.
- Eliminate some memory barriers added in a couple of previous revisions,
  after some discussion on port-alpha/tech-smp.

Still some lazy I-sync optimization possibilites:
- pmap_changebit() does not need to I-sync when only write-protecting
  a page.
- pmap_asn_alloc() may be able to cancel a pending lazy I-sync when a
  new ASN is allocated.  Need to double check against Green Book or
  Brown Book.
This commit is contained in:
thorpej 2000-08-15 05:21:20 +00:00
parent 33016f23e0
commit c7b8439ec4
1 changed files with 77 additions and 51 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: pmap.c,v 1.141 2000/08/13 22:43:42 thorpej Exp $ */
/* $NetBSD: pmap.c,v 1.142 2000/08/15 05:21:20 thorpej Exp $ */
/*-
* Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc.
@ -154,7 +154,7 @@
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.141 2000/08/13 22:43:42 thorpej Exp $");
__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.142 2000/08/15 05:21:20 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -624,6 +624,14 @@ do { \
} \
} while (0)
/*
* PMAP_SET_NEEDISYNC:
*
* Mark that a user pmap needs an I-stream synch on its
* way back out to userspace.
*/
#define PMAP_SET_NEEDISYNC(pmap) (pmap)->pm_needisync = ~0UL
/*
* PMAP_SYNC_ISTREAM:
*
@ -640,13 +648,12 @@ do { \
#define PMAP_SYNC_ISTREAM_USER(pmap) \
do { \
(pmap)->pm_needisync = ~0UL; \
alpha_mb(); /* XXX alpha_wmb()? */ \
alpha_multicast_ipi((pmap)->pm_cpus, ALPHA_IPI_AST); \
/* for curcpu, will happen in userret() */ \
} while (0)
#else
#define PMAP_SYNC_ISTREAM_KERNEL() alpha_pal_imb()
#define PMAP_SYNC_ISTREAM_USER(pmap) (pmap)->pm_needisync = ~0UL
#define PMAP_SYNC_ISTREAM_USER(pmap) /* will happen in userret() */
#endif /* MULTIPROCESSOR */
#define PMAP_SYNC_ISTREAM(pmap) \
@ -742,15 +749,7 @@ do { \
*
* Set a PTE to a specified value.
*/
#if defined(MULTIPROCESSOR)
#define PMAP_SET_PTE(ptep, val) \
do { \
*(ptep) = (val); \
alpha_mb(); /* XXX alpha_wmb()? */ \
} while (0)
#else
#define PMAP_SET_PTE(ptep, val) *(ptep) = (val)
#endif
/*
* PMAP_STAT_{INCR,DECR}:
@ -1639,7 +1638,8 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
paddr_t opa;
boolean_t tflush = TRUE;
boolean_t hadasm = FALSE; /* XXX gcc -Wuninitialized */
boolean_t needisync;
boolean_t needisync = FALSE;
boolean_t setisync = FALSE;
boolean_t isactive;
boolean_t wired;
long cpu_id = cpu_number();
@ -1655,10 +1655,23 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
managed = PAGE_IS_MANAGED(pa);
isactive = PMAP_ISACTIVE(pmap, cpu_id);
needisync = ((prot & VM_PROT_EXECUTE) != 0) &&
(pmap->pm_cpus != 0 || pmap == pmap_kernel());
wired = (flags & PMAP_WIRED) != 0;
/*
* Determine what we need to do about the I-stream. If
* VM_PROT_EXECUTE is set, we mark a user pmap as needing
* an I-sync on the way back out to userspace. We always
* need an immediate I-sync for the kernel pmap.
*/
if (prot & VM_PROT_EXECUTE) {
if (pmap == pmap_kernel())
needisync = TRUE;
else {
setisync = TRUE;
needisync = (pmap->pm_cpus != 0);
}
}
PMAP_MAP_TO_HEAD_LOCK();
PMAP_LOCK(pmap);
@ -1772,7 +1785,7 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
* No need to synchronize the I-stream, either, for basically
* the same reason.
*/
needisync = FALSE;
setisync = needisync = FALSE;
if (pmap != pmap_kernel()) {
/*
@ -1917,6 +1930,8 @@ pmap_enter(pmap_t pmap, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
pmap_tlb_shootdown(pmap, va, hadasm ? PG_ASM : 0);
#endif
}
if (setisync)
PMAP_SET_NEEDISYNC(pmap);
if (needisync)
PMAP_SYNC_ISTREAM(pmap);
@ -1976,6 +1991,9 @@ pmap_kenter_pa(vaddr_t va, paddr_t pa, vm_prot_t prot)
* Set the new PTE.
*/
PMAP_SET_PTE(pte, npte);
#if defined(MULTIPROCESSOR)
alpha_mb(); /* XXX alpha_wmb()? */
#endif
/*
* Invalidate the TLB entry for this VA and any appropriate
@ -2053,6 +2071,9 @@ pmap_kremove(vaddr_t va, vsize_t size)
/* Zap the mapping. */
PMAP_SET_PTE(pte, PG_NV);
#if defined(MULTIPROCESSOR)
alpha_mb(); /* XXX alpha_wmb()? */
#endif
PMAP_INVALIDATE_TLB(pmap, va, TRUE, TRUE, cpu_id);
#if defined(MULTIPROCESSOR) && 0
pmap_tlb_shootdown(pmap, va, PG_ASM);
@ -2356,10 +2377,6 @@ pmap_zero_page(paddr_t phys)
: "0" (p0), "1" (p1)
: "memory");
} while (p0 < pend);
#if defined(MULTIPROCESSOR)
/* Ensure the other CPUs see the correct data. */
alpha_mb(); /* XXX alpha_wmb()? */
#endif
}
/*
@ -2383,10 +2400,6 @@ pmap_copy_page(paddr_t src, paddr_t dst)
s = (caddr_t)ALPHA_PHYS_TO_K0SEG(src);
d = (caddr_t)ALPHA_PHYS_TO_K0SEG(dst);
memcpy(d, s, PAGE_SIZE);
#if defined(MULTIPROCESSOR)
/* Ensure the other CPUs see the correct data. */
alpha_mb(); /* XXX alpha_wmb()? */
#endif
}
/*
@ -2592,8 +2605,8 @@ alpha_protection_init(void)
* from beneath it. We assume that the pmap itself is already
* locked; dolock applies only to the PV list.
*
* Returns TRUE or FALSE, indicating if the I-stream needs to
* be synchronized.
* Returns TRUE or FALSE, indicating if an I-stream sync needs
* to be initiated (for this CPU or for other CPUs).
*/
boolean_t
pmap_remove_mapping(pmap_t pmap, vaddr_t va, pt_entry_t *pte,
@ -2603,7 +2616,7 @@ pmap_remove_mapping(pmap_t pmap, vaddr_t va, pt_entry_t *pte,
boolean_t onpv;
boolean_t hadasm;
boolean_t isactive;
boolean_t needisync;
boolean_t needisync = FALSE;
struct pv_entry **pvp;
pt_entry_t **ptp;
@ -2640,8 +2653,21 @@ pmap_remove_mapping(pmap_t pmap, vaddr_t va, pt_entry_t *pte,
onpv = (pmap_pte_pv(pte) != 0);
hadasm = (pmap_pte_asm(pte) != 0);
isactive = PMAP_ISACTIVE(pmap, cpu_id);
needisync = (pmap_pte_exec(pte) != 0) &&
(pmap->pm_cpus != 0 || pmap == pmap_kernel());
/*
* Determine what we need to do about the I-stream. If
* PG_EXEC was set, we mark a user pmap as needing an
* I-sync on the way out to userspace. We always need
* an immediate I-sync for the kernel pmap.
*/
if (pmap_pte_exec(pte)) {
if (pmap == pmap_kernel())
needisync = TRUE;
else {
PMAP_SET_NEEDISYNC(pmap);
needisync = (pmap->pm_cpus != 0);
}
}
/*
* Update statistics
@ -2751,16 +2777,25 @@ pmap_changebit(paddr_t pa, u_long set, u_long mask, long cpu_id)
if (*pte != npte) {
hadasm = (pmap_pte_asm(pte) != 0);
isactive = PMAP_ISACTIVE(pv->pv_pmap, cpu_id);
needisync = (pmap_pte_exec(pte) != 0) &&
(pv->pv_pmap->pm_cpus != 0 ||
pv->pv_pmap == pmap_kernel());
PMAP_SET_PTE(pte, npte);
if (needisync) {
/*
* Determine what we need to do about the I-stream.
* If PG_EXEC was set, we mark a user pmap as needing
* an I-sync on the way out to userspace. We always
* need an immediate I-sync for the kernel pmap.
*/
needisync = FALSE;
if (pmap_pte_exec(pte)) {
if (pv->pv_pmap == pmap_kernel())
needkisync |= TRUE;
else
PMAP_SYNC_ISTREAM_USER(pv->pv_pmap);
needkisync = TRUE;
else {
PMAP_SET_NEEDISYNC(pv->pv_pmap);
if (pv->pv_pmap->pm_cpus != 0)
needisync = TRUE;
}
}
PMAP_SET_PTE(pte, npte);
if (needisync)
PMAP_SYNC_ISTREAM_USER(pv->pv_pmap);
PMAP_INVALIDATE_TLB(pv->pv_pmap, va, hadasm, isactive,
cpu_id);
#if defined(MULTIPROCESSOR) && 0
@ -3111,11 +3146,6 @@ pmap_pv_alloc(void)
if (pvpmap == pmap_kernel())
continue;
/*
* XXX We know we're not going to try and
* XXX lock the kernel pmap, so we don't
* XXX have to block interrupts here.
*/
if (simple_lock_try(&pvpmap->pm_slock) == 0)
continue;
@ -3135,7 +3165,7 @@ pmap_pv_alloc(void)
*/
if (pmap_remove_mapping(pvpmap, pv->pv_va,
pte, FALSE, cpu_id, &prmt))
alpha_pal_imb(); /* XXXSMP */
PMAP_SYNC_ISTREAM(pvpmap);
/* Unlock everything and return. */
simple_unlock(&pvpmap->pm_slock);
@ -3627,14 +3657,10 @@ pmap_ptpage_steal(pmap_t pmap, int usage, paddr_t *pap)
}
}
PMAP_UNLOCK(spmap);
if (needisync)
PMAP_SYNC_ISTREAM(pmap);
if (needisync) { /* XXXSMP */
alpha_pal_imb();
#if defined(MULTIPROCESSOR) && 0
alpha_broadcast_ipi(ALPHA_IPI_IMB);
#endif
}
PMAP_UNLOCK(spmap);
#ifdef DIAGNOSTIC
if (prmt.prmt_ptp == NULL)