Reduce the frequency that the PowerPC/AIM pmaps invalidate instruction

caches, by invalidating kernel icaches only when needed and not flushing
user caches for shared pages.

Suggested by:	kib
MFC after:	2 weeks
This commit is contained in:
nwhitehorn 2012-04-06 16:03:38 +00:00
parent e05a39a26d
commit 9c79ae8eea
4 changed files with 33 additions and 57 deletions

View File

@ -405,6 +405,9 @@ powerpc_init(vm_offset_t startkernel, vm_offset_t endkernel,
cacheline_size = 32; cacheline_size = 32;
} }
/* Make sure the kernel icache is valid before we go too much further */
__syncicache((caddr_t)startkernel, endkernel - startkernel);
#ifndef __powerpc64__ #ifndef __powerpc64__
/* /*
* Figure out whether we need to use the 64 bit PMAP. This works by * Figure out whether we need to use the 64 bit PMAP. This works by

View File

@ -1087,7 +1087,7 @@ moea_enter_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
struct pvo_head *pvo_head; struct pvo_head *pvo_head;
uma_zone_t zone; uma_zone_t zone;
vm_page_t pg; vm_page_t pg;
u_int pte_lo, pvo_flags, was_exec; u_int pte_lo, pvo_flags;
int error; int error;
if (!moea_initialized) { if (!moea_initialized) {
@ -1095,13 +1095,11 @@ moea_enter_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
zone = moea_upvo_zone; zone = moea_upvo_zone;
pvo_flags = 0; pvo_flags = 0;
pg = NULL; pg = NULL;
was_exec = PTE_EXEC;
} else { } else {
pvo_head = vm_page_to_pvoh(m); pvo_head = vm_page_to_pvoh(m);
pg = m; pg = m;
zone = moea_mpvo_zone; zone = moea_mpvo_zone;
pvo_flags = PVO_MANAGED; pvo_flags = PVO_MANAGED;
was_exec = 0;
} }
if (pmap_bootstrapped) if (pmap_bootstrapped)
mtx_assert(&vm_page_queue_mtx, MA_OWNED); mtx_assert(&vm_page_queue_mtx, MA_OWNED);
@ -1117,18 +1115,6 @@ moea_enter_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
zone = moea_upvo_zone; zone = moea_upvo_zone;
} }
/*
* If this is a managed page, and it's the first reference to the page,
* clear the execness of the page. Otherwise fetch the execness.
*/
if ((pg != NULL) && ((m->oflags & VPO_UNMANAGED) == 0)) {
if (LIST_EMPTY(pvo_head)) {
moea_attr_clear(pg, PTE_EXEC);
} else {
was_exec = moea_attr_fetch(pg) & PTE_EXEC;
}
}
pte_lo = moea_calc_wimg(VM_PAGE_TO_PHYS(m), pmap_page_get_memattr(m)); pte_lo = moea_calc_wimg(VM_PAGE_TO_PHYS(m), pmap_page_get_memattr(m));
if (prot & VM_PROT_WRITE) { if (prot & VM_PROT_WRITE) {
@ -1149,22 +1135,12 @@ moea_enter_locked(pmap_t pmap, vm_offset_t va, vm_page_t m, vm_prot_t prot,
pte_lo, pvo_flags); pte_lo, pvo_flags);
/* /*
* Flush the real page from the instruction cache if this page is * Flush the real page from the instruction cache. This has be done
* mapped executable and cacheable and was not previously mapped (or * for all user mappings to prevent information leakage via the
* was not mapped executable). * instruction cache.
*/ */
if (error == 0 && (pvo_flags & PVO_EXECUTABLE) && if (pmap != kernel_pmap && LIST_EMPTY(vm_page_to_pvoh(m)))
(pte_lo & PTE_I) == 0 && was_exec == 0) {
/*
* Flush the real memory from the cache.
*/
moea_syncicache(VM_PAGE_TO_PHYS(m), PAGE_SIZE); moea_syncicache(VM_PAGE_TO_PHYS(m), PAGE_SIZE);
if (pg != NULL)
moea_attr_save(pg, PTE_EXEC);
}
/* XXX syncicache always until problems are sorted */
moea_syncicache(VM_PAGE_TO_PHYS(m), PAGE_SIZE);
} }
/* /*
@ -1486,12 +1462,6 @@ moea_kenter_attr(mmu_t mmu, vm_offset_t va, vm_offset_t pa, vm_memattr_t ma)
panic("moea_kenter: failed to enter va %#x pa %#x: %d", va, panic("moea_kenter: failed to enter va %#x pa %#x: %d", va,
pa, error); pa, error);
/*
* Flush the real memory from the instruction cache.
*/
if ((pte_lo & (PTE_I | PTE_G)) == 0) {
moea_syncicache(pa, PAGE_SIZE);
}
PMAP_UNLOCK(kernel_pmap); PMAP_UNLOCK(kernel_pmap);
} }

View File

@ -1246,8 +1246,11 @@ moea64_enter_locked(mmu_t mmu, pmap_t pmap, vm_offset_t va, vm_page_t m,
* Flush the page from the instruction cache if this page is * Flush the page from the instruction cache if this page is
* mapped executable and cacheable. * mapped executable and cacheable.
*/ */
if ((pte_lo & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) if (pmap != kernel_pmap && !(m->aflags & PGA_EXECUTABLE) &&
(pte_lo & (LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) {
vm_page_aflag_set(m, PGA_EXECUTABLE);
moea64_syncicache(mmu, pmap, va, VM_PAGE_TO_PHYS(m), PAGE_SIZE); moea64_syncicache(mmu, pmap, va, VM_PAGE_TO_PHYS(m), PAGE_SIZE);
}
} }
static void static void
@ -1670,12 +1673,6 @@ moea64_kenter_attr(mmu_t mmu, vm_offset_t va, vm_offset_t pa, vm_memattr_t ma)
if (error != 0 && error != ENOENT) if (error != 0 && error != ENOENT)
panic("moea64_kenter: failed to enter va %#zx pa %#zx: %d", va, panic("moea64_kenter: failed to enter va %#zx pa %#zx: %d", va,
pa, error); pa, error);
/*
* Flush the memory from the instruction cache.
*/
if ((pte_lo & (LPTE_I | LPTE_G)) == 0)
__syncicache((void *)va, PAGE_SIZE);
} }
void void
@ -1906,6 +1903,7 @@ static void
moea64_pvo_protect(mmu_t mmu, pmap_t pm, struct pvo_entry *pvo, vm_prot_t prot) moea64_pvo_protect(mmu_t mmu, pmap_t pm, struct pvo_entry *pvo, vm_prot_t prot)
{ {
uintptr_t pt; uintptr_t pt;
struct vm_page *pg;
uint64_t oldlo; uint64_t oldlo;
PMAP_LOCK_ASSERT(pm, MA_OWNED); PMAP_LOCK_ASSERT(pm, MA_OWNED);
@ -1929,17 +1927,21 @@ moea64_pvo_protect(mmu_t mmu, pmap_t pm, struct pvo_entry *pvo, vm_prot_t prot)
else else
pvo->pvo_pte.lpte.pte_lo |= LPTE_BR; pvo->pvo_pte.lpte.pte_lo |= LPTE_BR;
pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN);
/* /*
* If the PVO is in the page table, update that pte as well. * If the PVO is in the page table, update that pte as well.
*/ */
if (pt != -1) { if (pt != -1) {
MOEA64_PTE_CHANGE(mmu, pt, &pvo->pvo_pte.lpte, MOEA64_PTE_CHANGE(mmu, pt, &pvo->pvo_pte.lpte,
pvo->pvo_vpn); pvo->pvo_vpn);
if ((pvo->pvo_pte.lpte.pte_lo & if (pm != kernel_pmap && pg != NULL &&
(LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) { !(pg->aflags & PGA_EXECUTABLE) &&
(pvo->pvo_pte.lpte.pte_lo &
(LPTE_I | LPTE_G | LPTE_NOEXEC)) == 0) {
vm_page_aflag_set(pg, PGA_EXECUTABLE);
moea64_syncicache(mmu, pm, PVO_VADDR(pvo), moea64_syncicache(mmu, pm, PVO_VADDR(pvo),
pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN, pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN, PAGE_SIZE);
PAGE_SIZE);
} }
} }
@ -1949,9 +1951,6 @@ moea64_pvo_protect(mmu_t mmu, pmap_t pm, struct pvo_entry *pvo, vm_prot_t prot)
*/ */
if ((pvo->pvo_vaddr & PVO_MANAGED) == PVO_MANAGED && if ((pvo->pvo_vaddr & PVO_MANAGED) == PVO_MANAGED &&
(oldlo & LPTE_PP) != LPTE_BR && !(prot && VM_PROT_WRITE)) { (oldlo & LPTE_PP) != LPTE_BR && !(prot && VM_PROT_WRITE)) {
struct vm_page *pg;
pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN);
if (pg != NULL) { if (pg != NULL) {
if (pvo->pvo_pte.lpte.pte_lo & LPTE_CHG) if (pvo->pvo_pte.lpte.pte_lo & LPTE_CHG)
vm_page_dirty(pg); vm_page_dirty(pg);
@ -2134,15 +2133,11 @@ moea64_remove(mmu_t mmu, pmap_t pm, vm_offset_t sva, vm_offset_t eva)
void void
moea64_remove_all(mmu_t mmu, vm_page_t m) moea64_remove_all(mmu_t mmu, vm_page_t m)
{ {
struct pvo_head *pvo_head;
struct pvo_entry *pvo, *next_pvo; struct pvo_entry *pvo, *next_pvo;
pmap_t pmap; pmap_t pmap;
pvo_head = vm_page_to_pvoh(m);
LOCK_TABLE_WR(); LOCK_TABLE_WR();
for (pvo = LIST_FIRST(pvo_head); pvo != NULL; pvo = next_pvo) { LIST_FOREACH_SAFE(pvo, vm_page_to_pvoh(m), pvo_vlink, next_pvo) {
next_pvo = LIST_NEXT(pvo, pvo_vlink);
pmap = pvo->pvo_pmap; pmap = pvo->pvo_pmap;
PMAP_LOCK(pmap); PMAP_LOCK(pmap);
moea64_pvo_remove(mmu, pvo); moea64_pvo_remove(mmu, pvo);
@ -2152,6 +2147,7 @@ moea64_remove_all(mmu_t mmu, vm_page_t m)
if ((m->aflags & PGA_WRITEABLE) && moea64_is_modified(mmu, m)) if ((m->aflags & PGA_WRITEABLE) && moea64_is_modified(mmu, m))
vm_page_dirty(m); vm_page_dirty(m);
vm_page_aflag_clear(m, PGA_WRITEABLE); vm_page_aflag_clear(m, PGA_WRITEABLE);
vm_page_aflag_clear(m, PGA_EXECUTABLE);
} }
/* /*
@ -2356,6 +2352,7 @@ moea64_pvo_enter(mmu_t mmu, pmap_t pm, uma_zone_t zone,
static void static void
moea64_pvo_remove(mmu_t mmu, struct pvo_entry *pvo) moea64_pvo_remove(mmu_t mmu, struct pvo_entry *pvo)
{ {
struct vm_page *pg;
uintptr_t pt; uintptr_t pt;
PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED); PMAP_LOCK_ASSERT(pvo->pvo_pmap, MA_OWNED);
@ -2395,11 +2392,10 @@ moea64_pvo_remove(mmu_t mmu, struct pvo_entry *pvo)
/* /*
* Update vm about the REF/CHG bits if the page is managed. * Update vm about the REF/CHG bits if the page is managed.
*/ */
pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN);
if ((pvo->pvo_vaddr & PVO_MANAGED) == PVO_MANAGED && if ((pvo->pvo_vaddr & PVO_MANAGED) == PVO_MANAGED &&
(pvo->pvo_pte.lpte.pte_lo & LPTE_PP) != LPTE_BR) { (pvo->pvo_pte.lpte.pte_lo & LPTE_PP) != LPTE_BR) {
struct vm_page *pg;
pg = PHYS_TO_VM_PAGE(pvo->pvo_pte.lpte.pte_lo & LPTE_RPGN);
if (pg != NULL) { if (pg != NULL) {
if (pvo->pvo_pte.lpte.pte_lo & LPTE_CHG) if (pvo->pvo_pte.lpte.pte_lo & LPTE_CHG)
vm_page_dirty(pg); vm_page_dirty(pg);
@ -2410,6 +2406,9 @@ moea64_pvo_remove(mmu_t mmu, struct pvo_entry *pvo)
} }
} }
if (pg != NULL && LIST_EMPTY(vm_page_to_pvoh(pg)))
vm_page_aflag_clear(pg, PGA_EXECUTABLE);
moea64_pvo_entries--; moea64_pvo_entries--;
moea64_pvo_remove_calls++; moea64_pvo_remove_calls++;

View File

@ -248,9 +248,13 @@ extern struct vpglocks pa_lock[];
* *
* PGA_WRITEABLE is set exclusively on managed pages by pmap_enter(). When it * PGA_WRITEABLE is set exclusively on managed pages by pmap_enter(). When it
* does so, the page must be VPO_BUSY. * does so, the page must be VPO_BUSY.
*
* PGA_EXECUTABLE may be set by pmap routines, and indicates that a page has
* at least one executable mapping. It is not consumed by the VM layer.
*/ */
#define PGA_WRITEABLE 0x01 /* page may be mapped writeable */ #define PGA_WRITEABLE 0x01 /* page may be mapped writeable */
#define PGA_REFERENCED 0x02 /* page has been referenced */ #define PGA_REFERENCED 0x02 /* page has been referenced */
#define PGA_EXECUTABLE 0x04 /* page may be mapped executable */
/* /*
* Page flags. If changed at any other time than page allocation or * Page flags. If changed at any other time than page allocation or