Files
Arquivotheca.Solaris-2.5/uts/sun4e/os/ppage.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

296 lines
6.6 KiB
C
Executable File

/*
* Copyright (c) 1990-1992, 1993 by Sun Microsystems, Inc.
*/
#pragma ident "@(#)ppage.c 1.6 93/11/11 SMI"
#include <sys/t_lock.h>
#include <sys/systm.h>
#include <sys/map.h>
#include <sys/mman.h>
#include <sys/vm.h>
#include <sys/mmu.h>
#include <sys/cpu.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/machsystm.h>
#include <vm/page.h>
#include <vm/seg.h>
#include <vm/seg_kmem.h>
#include <vm/hat_sunm.h>
/*
* A quick way to generate a cache consistent address
* to map in a page.
* users: ppcopy, pagezero, /proc, dev/mem
*
* VACSZPG - size of vac in pages
* NSETS - number of multiple pages that are can be mapped
* consistently, must be a power of two
*/
#define VACSZPG 8
#define VACSHIFT 16
#define NSETS 4
static caddr_t ppmap_vaddrs[VACSZPG][NSETS];
static int ppmap_last = 0;
#ifdef PPDEBUG
static int ppalloc_map = 0;
static int ppalloc_nomap = 0;
static int ppalloc_noslot = 0;
static int nset_hits[NSETS+1];
static int align_hits[VACSZPG+1];
static int align_miss[VACSZPG+1];
static int avoided = 0;
#endif PPDEBUG
static kmutex_t ppmap_lock;
void
ppmapinit()
{
register int align, nset;
register caddr_t va;
mutex_init(&ppmap_lock, "ppmap_lock", MUTEX_DEFAULT, NULL);
mutex_enter(&ppmap_lock);
va = (caddr_t)PPMAPBASE;
for (align = 0; align < VACSZPG; align++) {
for (nset = 0; nset < NSETS; nset++) {
ppmap_vaddrs[align][nset] =
va + nset * vac_size;
}
va += MMU_PAGESIZE;
}
mutex_exit(&ppmap_lock);
}
/*
* Allocate a cache consistent virtual address to map a page, pp,
* with protection, vprot; and map it in the MMU, using the most
* efficient means possible. The argument avoid is a virtual address
* hint which when masked yields an offset into a virtual cache
* that should be avoided when allocating an address to map in a
* page. An avoid arg of -1 means you don't care, for instance pagezero.
*
* machine dependent, depends on virtual address space layout,
* understands that all kernel addresses have bit 31 set.
*/
caddr_t
ppmapin(pp, vprot, avoid)
register struct page *pp;
register u_int vprot;
register caddr_t avoid;
{
register int align, av_align, nset;
register caddr_t va = NULL;
union {
struct pte u_pte;
int u_ipte;
} pte;
u_long a;
mutex_enter(&ppmap_lock);
if (pp->p_mapping && vac) {
/*
* Find an unused cache consistent slot to map the page in.
*/
align = pp->p_mapping->hme_impl & vac_mask >> MMU_PAGESHIFT;
for (nset = 0; nset < NSETS; nset++) {
if ((int)ppmap_vaddrs[align][nset] < 0) {
#ifdef PPDEBUG
nset_hits[nset]++;
align_hits[align]++;
#endif PPDEBUG
va = ppmap_vaddrs[align][nset];
break;
}
}
} else {
/*
* The page has no mappings, common COW case.
* Avoid using a mapping that collides with
* the avoid address parameter in the cache.
* We can pick any alignment except the one we are
* supposed to avoid, if the first pick isn't
* avaliable we try others.
*/
align = ppmap_last++;
if (ppmap_last >= VACSZPG)
ppmap_last = 0;
if (avoid != (caddr_t)-1) {
av_align = ((u_int)avoid & vac_mask) >> MMU_PAGESHIFT;
if (align == av_align) {
#ifdef PPDEBUG
avoided++;
#endif PPDEBUG
if (++align >= VACSZPG)
align = 0;
}
}
do {
for (nset = 0; nset < NSETS; nset++)
if ((int)ppmap_vaddrs[align][nset] < 0) {
#ifdef PPDEBUG
nset_hits[nset]++;
align_hits[align]++;
#endif PPDEBUG
va = ppmap_vaddrs[align][nset];
break;
}
if (va)
break;
/*
* first pick didn't succeed, try another
*/
if (++align >= VACSZPG)
align = 0;
} while (align != ppmap_last);
}
if (va == NULL) {
#ifdef PPDEBUG
ppalloc_noslot++;
align_miss[align]++;
#endif PPDEBUG
/*
* No free slots; get a random one from kernelmap.
*/
mutex_exit(&ppmap_lock);
a = rmalloc_wait(kernelmap, (long)CLSIZE);
va = kmxtob(a);
sunm_mempte(pp, vprot, &pte.u_pte);
sunm_pteload(&kas, va, pp, pte.u_pte,
PTELD_LOCK | PTELD_NOSYNC);
} else if (!pp->p_mapping) {
#ifdef PPDEBUG
ppalloc_nomap++;
#endif PPDEBUG
/*
* page had no mappings, short circuit everything.
*/
ppmap_vaddrs[align][nset] = (caddr_t)1;
mutex_exit(&ppmap_lock);
pte.u_ipte = 0;
pte.u_pte.pg_prot = sunm_vtop_prot(vprot);
pte.u_ipte |= (PG_V | PGT_OBMEM | page_pptonum(pp));
mmu_setpte(va, pte.u_pte);
} else {
#ifdef PPDEBUG
ppalloc_map++;
#endif PPDEBUG
/*
* Page had mappings, use the hat layer.
*/
ppmap_vaddrs[align][nset] = (caddr_t)2;
mutex_exit(&ppmap_lock);
sunm_mempte(pp, vprot, &pte.u_pte);
sunm_pteload(&kas, va, pp, pte.u_pte,
PTELD_LOCK | PTELD_NOSYNC);
}
return (va);
}
void
ppmapout(va)
register caddr_t va;
{
register int align, nset;
if (va > Sysbase) {
/*
* Space came from kernelmap, flush the page and
* return the space.
*/
sunm_unload(&kas, va, PAGESIZE, HAT_UNLOAD);
rmfree(kernelmap, (size_t)CLSIZE, (ulong_t)btokmx(va));
} else {
/*
* Space came from ppmap_vaddrs[], give it back.
*/
align = ((u_int)va & vac_mask) >> MMU_PAGESHIFT;
nset = ((int)va & ((NSETS - 1) << VACSHIFT)) >> VACSHIFT;
if (ppmap_vaddrs[align][nset] == (caddr_t)1) {
vac_pageflush(va);
mmu_setpte(va, mmu_pteinvalid);
} else if (ppmap_vaddrs[align][nset] == (caddr_t)2) {
sunm_unload(&kas, va, PAGESIZE, HAT_UNLOAD);
} else
cmn_err(CE_PANIC, "ppmapout");
mutex_enter(&ppmap_lock);
ppmap_vaddrs[align][nset] = va;
mutex_exit(&ppmap_lock);
}
}
/*
* Copy the data from the physical page represented by "frompp" to
* that represented by "topp".
*/
void
ppcopy(frompp, topp)
page_t *frompp;
page_t *topp;
{
register caddr_t from_va, to_va;
ASSERT(se_assert(&frompp->p_selock));
ASSERT(se_assert(&topp->p_selock));
from_va = ppmapin(frompp, PROT_READ, (caddr_t)-1);
to_va = ppmapin(topp, PROT_READ | PROT_WRITE, from_va);
pgcopy(from_va, to_va, PAGESIZE);
ppmapout(from_va);
ppmapout(to_va);
}
/*
* Zero the physical page from off to off + len given by `pp'
* without changing the reference and modified bits of page.
*/
void
pagezero(pp, off, len)
page_t *pp;
u_int off, len;
{
register caddr_t va;
ASSERT((int)len > 0 && (int)off >= 0 && off + len <= PAGESIZE);
ASSERT(se_assert(&pp->p_selock));
va = ppmapin(pp, PROT_READ | PROT_WRITE, (caddr_t)-1);
bzero(va + off, len);
ppmapout(va);
}
#ifdef PPDEBUG
/*
* Calculate the checksum for the physical page given by `pp'
* without changing the reference and modified bits of page.
*/
int
pagesum(pp)
struct page *pp;
{
register caddr_t va;
register int checksum = 0;
register int *last, *ip;
va = ppmapin(pp, PROT_READ, (caddr_t)-1);
last = (int *)(va + PAGESIZE);
for (ip = (int *)va; ip < last; ip++)
checksum += *ip;
ppmapout(va);
return (checksum);
}
#endif PPDEBUG