diff options
Diffstat (limited to 'sys/arch/amd64/amd64/pmap.c')
-rw-r--r-- | sys/arch/amd64/amd64/pmap.c | 783 |
1 files changed, 78 insertions, 705 deletions
diff --git a/sys/arch/amd64/amd64/pmap.c b/sys/arch/amd64/amd64/pmap.c index 3c046b12986..bbb40e87308 100644 --- a/sys/arch/amd64/amd64/pmap.c +++ b/sys/arch/amd64/amd64/pmap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pmap.c,v 1.8 2005/05/24 21:11:47 tedu Exp $ */ +/* $OpenBSD: pmap.c,v 1.9 2005/05/27 19:32:39 art Exp $ */ /* $NetBSD: pmap.c,v 1.3 2003/05/08 18:13:13 thorpej Exp $ */ /* @@ -169,9 +169,6 @@ * page is mapped in. this is critical for page based operations * such as pmap_page_protect() [change protection on _all_ mappings * of a page] - * - pv_page/pv_page_info: pv_entry's are allocated out of pv_page's. - * if we run out of pv_entry's we allocate a new pv_page and free - * its pv_entrys. * - pmap_remove_record: a list of virtual addresses whose mappings * have been changed. used for TLB flushing. */ @@ -209,21 +206,7 @@ * pmap_growkernel() to grow the kernel PTPs in advance. * * [C] pv_entry structures - * - plan 1: try to allocate one off the free list - * => success: done! - * => failure: no more free pv_entrys on the list - * - plan 2: try to allocate a new pv_page to add a chunk of - * pv_entrys to the free list - * [a] obtain a free, unmapped, VA in kmem_map. either - * we have one saved from a previous call, or we allocate - * one now using a "vm_map_lock_try" in uvm_map - * => success: we have an unmapped VA, continue to [b] - * => failure: unable to lock kmem_map or out of VA in it. - * move on to plan 3. - * [b] allocate a page for the VA - * => success: map it in, free the pv_entry's, DONE! - * => failure: no free vm_pages, etc. - * save VA for later call to [a], go to plan 3. + * - try to allocate one from the pool. * If we fail, we simply let pmap_enter() tell UVM about it. */ @@ -261,10 +244,6 @@ * when traversing the list (e.g. adding/removing mappings, * syncing R/M bits, etc.) * - * - pvalloc_lock - * this lock protects the data structures which are used to manage - * the free list of pv_entry structures. - * * - pmaps_lock * this lock protects the list of active pmaps (headed by "pmaps"). * we lock it when adding or removing pmaps from this list. @@ -291,7 +270,6 @@ paddr_t DMPDpa; /* int nkpde = NKPTP; */ -struct simplelock pvalloc_lock; struct simplelock pmaps_lock; #if (defined(MULTIPROCESSOR) || defined(LOCKDEBUG)) && 0 @@ -397,19 +375,9 @@ pt_entry_t protection_codes[8]; /* maps MI prot to i386 prot code */ boolean_t pmap_initialized = FALSE; /* pmap_init done yet? */ /* - * pv_page management structures: locked by pvalloc_lock + * pv management structures. */ - -TAILQ_HEAD(pv_pagelist, pv_page); -struct pv_pagelist pv_freepages; /* list of pv_pages with free entrys */ -struct pv_pagelist pv_unusedpgs; /* list of unused pv_pages */ -unsigned int pv_nfpvents; /* # of free pv entries */ -struct pv_page *pv_initpage; /* bootstrap page from kernel_map */ -vaddr_t pv_cachedva; /* cached VA for later use */ - -#define PVE_LOWAT (PVE_PER_PVPAGE / 2) /* free pv_entry low water mark */ -#define PVE_HIWAT (PVE_LOWAT + (PVE_PER_PVPAGE * 2)) - /* high water mark */ +struct pool pmap_pv_pool; /* * linked list of all non-kernel pmaps @@ -425,26 +393,6 @@ struct pool pmap_pmap_pool; /* - * MULTIPROCESSOR: special VA's/ PTE's are actually allocated inside a - * X86_MAXPROCS*NPTECL array of PTE's, to avoid cache line thrashing - * due to false sharing. - */ - -#ifdef MULTIPROCESSOR -#define PTESLEW(pte, id) ((pte)+(id)*NPTECL) -#define VASLEW(va,id) ((va)+(id)*NPTECL*PAGE_SIZE) -#else -#define PTESLEW(pte, id) (pte) -#define VASLEW(va,id) (va) -#endif - -/* - * special VAs and the PTEs that map them - */ -pt_entry_t *csrc_pte, *cdst_pte, *zero_pte, *ptp_pte, *early_zero_pte; -caddr_t csrcp, cdstp, zerop, ptpp, early_zerop; - -/* * pool and cache that PDPs are allocated from */ @@ -454,8 +402,6 @@ u_int pmap_pdp_cache_generation; int pmap_pdp_ctor(void *, void *, int); -caddr_t vmmap; /* XXX: used by mem.c... it should really uvm_map_reserve it */ - extern vaddr_t msgbuf_vaddr; extern paddr_t msgbuf_paddr; @@ -470,52 +416,33 @@ extern vaddr_t lo32_paddr; vaddr_t virtual_avail; extern int end; -#if defined(I586_CPU) -/* stuff to fix the pentium f00f bug */ -extern vaddr_t pentium_idt_vaddr; -#endif - /* * local prototypes */ -static struct pv_entry *pmap_add_pvpage(struct pv_page *, boolean_t); -static struct pv_entry *pmap_alloc_pv(struct pmap *, int); /* see codes below */ -#define ALLOCPV_NEED 0 /* need PV now */ -#define ALLOCPV_TRY 1 /* just try to allocate, don't steal */ -#define ALLOCPV_NONEED 2 /* don't need PV, just growing cache */ -struct pv_entry *pmap_alloc_pvpage(struct pmap *, int); -static void pmap_enter_pv(struct pv_head *, - struct pv_entry *, struct pmap *, vaddr_t, struct vm_page *); -static void pmap_free_pv(struct pmap *, struct pv_entry *); -static void pmap_free_pvs(struct pmap *, struct pv_entry *); -static void pmap_free_pv_doit(struct pv_entry *); -void pmap_free_pvpage(void); -struct vm_page *pmap_get_ptp(struct pmap *, vaddr_t, pd_entry_t **); -static struct vm_page *pmap_find_ptp(struct pmap *, vaddr_t, paddr_t, int); -void pmap_free_ptp(struct pmap *, struct vm_page *, +void pmap_enter_pv(struct pv_head *, struct pv_entry *, struct pmap *, + vaddr_t, struct vm_page *); +struct vm_page *pmap_get_ptp(struct pmap *, vaddr_t, pd_entry_t **); +struct vm_page *pmap_find_ptp(struct pmap *, vaddr_t, paddr_t, int); +void pmap_free_ptp(struct pmap *, struct vm_page *, vaddr_t, pt_entry_t *, pd_entry_t **, int32_t *); -static void pmap_freepage(struct pmap *, struct vm_page *, int); -static boolean_t pmap_is_curpmap(struct pmap *); +void pmap_freepage(struct pmap *, struct vm_page *, int); static boolean_t pmap_is_active(struct pmap *, int); -static void pmap_map_ptes(struct pmap *, pt_entry_t **, pd_entry_t ***); -static struct pv_entry *pmap_remove_pv(struct pv_head *, struct pmap *, vaddr_t); -void pmap_do_remove(struct pmap *, vaddr_t, vaddr_t, int); +void pmap_map_ptes(struct pmap *, pt_entry_t **, pd_entry_t ***); +struct pv_entry *pmap_remove_pv(struct pv_head *, struct pmap *, vaddr_t); +void pmap_do_remove(struct pmap *, vaddr_t, vaddr_t, int); boolean_t pmap_remove_pte(struct pmap *, struct vm_page *, pt_entry_t *, vaddr_t, int32_t *, int); -void pmap_remove_ptes(struct pmap *, struct vm_page *, vaddr_t, +void pmap_remove_ptes(struct pmap *, struct vm_page *, vaddr_t, vaddr_t, vaddr_t, int32_t *, int); #define PMAP_REMOVE_ALL 0 /* remove all mappings */ #define PMAP_REMOVE_SKIPWIRED 1 /* skip wired mappings */ -static vaddr_t pmap_tmpmap_pa(paddr_t); -static pt_entry_t *pmap_tmpmap_pvepte(struct pv_entry *); -static void pmap_tmpunmap_pa(void); -static void pmap_tmpunmap_pvepte(struct pv_entry *); -static void pmap_unmap_ptes(struct pmap *); +void pmap_unmap_ptes(struct pmap *); boolean_t pmap_get_physpage(vaddr_t, int, paddr_t *); boolean_t pmap_pdes_valid(vaddr_t, pd_entry_t **, pd_entry_t *); -void pmap_alloc_level(pd_entry_t **, vaddr_t, int, long *); +void pmap_alloc_level(pd_entry_t **, vaddr_t, int, long *); +void pmap_apte_flush(struct pmap *pmap); /* * p m a p i n l i n e h e l p e r f u n c t i o n s @@ -526,9 +453,8 @@ void pmap_alloc_level(pd_entry_t **, vaddr_t, int, long *); * of course the kernel is always loaded */ -__inline static boolean_t -pmap_is_curpmap(pmap) - struct pmap *pmap; +static __inline boolean_t +pmap_is_curpmap(struct pmap *pmap) { return((pmap == pmap_kernel()) || (pmap->pm_pdirpa == (paddr_t) rcr3())); @@ -538,101 +464,14 @@ pmap_is_curpmap(pmap) * pmap_is_active: is this pmap loaded into the specified processor's %cr3? */ -__inline static boolean_t -pmap_is_active(pmap, cpu_id) - struct pmap *pmap; - int cpu_id; +static __inline boolean_t +pmap_is_active(struct pmap *pmap, int cpu_id) { - return (pmap == pmap_kernel() || (pmap->pm_cpus & (1U << cpu_id)) != 0); } -/* - * pmap_tmpmap_pa: map a page in for tmp usage - */ - -__inline static vaddr_t -pmap_tmpmap_pa(pa) - paddr_t pa; -{ -#ifdef MULTIPROCESSOR - int id = cpu_number(); -#endif - pt_entry_t *ptpte = PTESLEW(ptp_pte, id); - caddr_t ptpva = VASLEW(ptpp, id); -#if defined(DIAGNOSTIC) - if (*ptpte) - panic("pmap_tmpmap_pa: ptp_pte in use?"); -#endif - *ptpte = PG_V | PG_RW | pa; /* always a new mapping */ - return((vaddr_t)ptpva); -} - -/* - * pmap_tmpunmap_pa: unmap a tmp use page (undoes pmap_tmpmap_pa) - */ - -__inline static void -pmap_tmpunmap_pa() -{ -#ifdef MULTIPROCESSOR - int id = cpu_number(); -#endif - pt_entry_t *ptpte = PTESLEW(ptp_pte, id); - caddr_t ptpva = VASLEW(ptpp, id); -#if defined(DIAGNOSTIC) - if (!pmap_valid_entry(*ptp_pte)) - panic("pmap_tmpunmap_pa: our pte invalid?"); -#endif - *ptpte = 0; /* zap! */ - pmap_update_pg((vaddr_t)ptpva); -#ifdef MULTIPROCESSOR - /* - * No need for tlb shootdown here, since ptp_pte is per-CPU. - */ -#endif -} - -/* - * pmap_tmpmap_pvepte: get a quick mapping of a PTE for a pv_entry - * - * => do NOT use this on kernel mappings [why? because pv_ptp may be NULL] - */ - -__inline static pt_entry_t * -pmap_tmpmap_pvepte(pve) - struct pv_entry *pve; -{ -#ifdef DIAGNOSTIC - if (pve->pv_pmap == pmap_kernel()) - panic("pmap_tmpmap_pvepte: attempt to map kernel"); -#endif - - /* is it current pmap? use direct mapping... */ - if (pmap_is_curpmap(pve->pv_pmap)) - return(vtopte(pve->pv_va)); - - return(((pt_entry_t *)pmap_tmpmap_pa(VM_PAGE_TO_PHYS(pve->pv_ptp))) - + ptei((unsigned long)pve->pv_va)); -} - -/* - * pmap_tmpunmap_pvepte: release a mapping obtained with pmap_tmpmap_pvepte - */ - -__inline static void -pmap_tmpunmap_pvepte(pve) - struct pv_entry *pve; -{ - /* was it current pmap? if so, return */ - if (pmap_is_curpmap(pve->pv_pmap)) - return; - - pmap_tmpunmap_pa(); -} - -__inline static void +void pmap_apte_flush(struct pmap *pmap) { #if defined(MULTIPROCESSOR) @@ -671,7 +510,7 @@ pmap_apte_flush(struct pmap *pmap) * => must be undone with pmap_unmap_ptes before returning */ -__inline static void +void pmap_map_ptes(pmap, ptepp, pdeppp) struct pmap *pmap; pt_entry_t **ptepp; @@ -719,9 +558,8 @@ pmap_map_ptes(pmap, ptepp, pdeppp) * pmap_unmap_ptes: unlock the PTE mapping of "pmap" */ -__inline static void -pmap_unmap_ptes(pmap) - struct pmap *pmap; +void +pmap_unmap_ptes(struct pmap *pmap) { if (pmap == pmap_kernel()) { return; @@ -854,7 +692,8 @@ pmap_bootstrap(vaddr_t kva_start, paddr_t max_pa) { vaddr_t kva, kva_end; struct pmap *kpm; - pt_entry_t *pte; + pt_entry_t *tmppte; + vaddr_t tmpva; int i; unsigned long p1i; pt_entry_t pg_nx = (cpu_feature & CPUID_NXE? PG_NX : 0); @@ -936,16 +775,13 @@ pmap_bootstrap(vaddr_t kva_start, paddr_t max_pa) if (pmap_valid_entry(PTE_BASE[p1i])) PTE_BASE[p1i] |= PG_G; } + /* - * zero_pte is stuck at the end of mapped space for the kernel - * image (disjunct from kva space). This is done so that it - * can safely be used in pmap_growkernel (pmap_get_physpage), - * when it's called for the first time. - * XXXfvdl fix this for MULTIPROCESSOR later. + * Temporary mapping for setting up the direct map. */ - - early_zerop = (caddr_t)(KERNBASE + NKL2_KIMG_ENTRIES * NBPD_L2); - early_zero_pte = PTE_BASE + pl1_i((unsigned long)early_zerop); + tmpva = (KERNBASE + NKL2_KIMG_ENTRIES * NBPD_L2); + virtual_avail += PAGE_SIZE; + tmppte = PTE_BASE + pl1_i(tmpva); /* * Enable large pages. @@ -972,10 +808,10 @@ pmap_bootstrap(vaddr_t kva_start, paddr_t max_pa) pdp = (paddr_t)&(((pd_entry_t *)dmpd)[i]); off = pdp - trunc_page(pdp); - *early_zero_pte = (trunc_page(pdp) & PG_FRAME) | PG_V | PG_RW; - pmap_update_pg((vaddr_t)early_zerop); + *tmppte = (trunc_page(pdp) & PG_FRAME) | PG_V | PG_RW; + pmap_update_pg(tmpva); - va = (vaddr_t)early_zerop + off; + va = tmpva + off; *((pd_entry_t *)va) = (paddr_t)i << L2_SHIFT; *((pd_entry_t *)va) |= PG_RW | PG_V | PG_PS | PG_G; } @@ -987,96 +823,35 @@ pmap_bootstrap(vaddr_t kva_start, paddr_t max_pa) pdp = (paddr_t)&(((pd_entry_t *)dmpdp)[i]); off = pdp - trunc_page(pdp); - *early_zero_pte = (trunc_page(pdp) & PG_FRAME) | PG_V | PG_RW; - pmap_update_pg((vaddr_t)early_zerop); + *tmppte = (trunc_page(pdp) & PG_FRAME) | PG_V | PG_RW; + pmap_update_pg(tmpva); - va = (vaddr_t)early_zerop + off; + va = tmpva + off; *((pd_entry_t *)va) = dmpd + (i << PAGE_SHIFT); *((pd_entry_t *)va) |= PG_RW | PG_V | PG_U; } + *tmppte = 0; DMPDpa = dmpdp; kpm->pm_pdir[PDIR_SLOT_DIRECT] = DMPDpa | PG_V | PG_KW | PG_U; tlbflush(); - /* - * now we allocate the "special" VAs which are used for tmp mappings - * by the pmap (and other modules). we allocate the VAs by advancing - * virtual_avail (note that there are no pages mapped at these VAs). - * we find the PTE that maps the allocated VA via the linear PTE - * mapping. - */ - - pte = PTE_BASE + pl1_i(virtual_avail); - -#ifdef MULTIPROCESSOR - /* - * Waste some VA space to avoid false sharing of cache lines - * for page table pages: Give each possible CPU a cache line - * of PTE's (8) to play with, though we only need 4. We could - * recycle some of this waste by putting the idle stacks here - * as well; we could waste less space if we knew the largest - * CPU ID beforehand. - */ - csrcp = (caddr_t) virtual_avail; csrc_pte = pte; - - cdstp = (caddr_t) virtual_avail+PAGE_SIZE; cdst_pte = pte+1; - - zerop = (caddr_t) virtual_avail+PAGE_SIZE*2; zero_pte = pte+2; - - ptpp = (caddr_t) virtual_avail+PAGE_SIZE*3; ptp_pte = pte+3; - - virtual_avail += PAGE_SIZE * X86_MAXPROCS * NPTECL; - pte += X86_MAXPROCS * NPTECL; -#else - csrcp = (caddr_t) virtual_avail; csrc_pte = pte; /* allocate */ - virtual_avail += PAGE_SIZE; pte++; /* advance */ - - cdstp = (caddr_t) virtual_avail; cdst_pte = pte; - virtual_avail += PAGE_SIZE; pte++; - - zerop = (caddr_t) virtual_avail; zero_pte = pte; - virtual_avail += PAGE_SIZE; pte++; - - ptpp = (caddr_t) virtual_avail; ptp_pte = pte; - virtual_avail += PAGE_SIZE; pte++; -#endif - -#if VM_MIN_KERNEL_ADDRESS == KERNBASE - early_zerop = zerop; - early_zero_pte = zero_pte; -#endif - - pte = (void *)0xdeadbeef; - - /* XXX: vmmap used by mem.c... should be uvm_map_reserve */ - /* XXXfvdl PTEs not needed here */ - vmmap = (char *)virtual_avail; /* don't need pte */ - virtual_avail += PAGE_SIZE; pte++; - - msgbuf_vaddr = virtual_avail; /* don't need pte */ + msgbuf_vaddr = virtual_avail; virtual_avail += round_page(MSGBUFSIZE); - pte += x86_btop(round_page(MSGBUFSIZE)); - idt_vaddr = virtual_avail; /* don't need pte */ - virtual_avail += 2 * PAGE_SIZE; pte += 2; + idt_vaddr = virtual_avail; + virtual_avail += 2 * PAGE_SIZE; idt_paddr = avail_start; /* steal a page */ avail_start += 2 * PAGE_SIZE; -#if defined(I586_CPU) - /* pentium f00f bug stuff */ - pentium_idt_vaddr = virtual_avail; /* don't need pte */ - virtual_avail += PAGE_SIZE; pte++; -#endif - #ifdef _LP64 /* * Grab a page below 4G for things that need it (i.e. * having an initial %cr3 for the MP trampoline). */ lo32_vaddr = virtual_avail; - virtual_avail += PAGE_SIZE; pte++; + virtual_avail += PAGE_SIZE; lo32_paddr = avail_start; avail_start += PAGE_SIZE; #endif @@ -1094,18 +869,17 @@ pmap_bootstrap(vaddr_t kva_start, paddr_t max_pa) #if (defined(MULTIPROCESSOR) || defined(LOCKDEBUG)) && 0 spinlockinit(&pmap_main_lock, "pmaplk", 0); #endif - simple_lock_init(&pvalloc_lock); simple_lock_init(&pmaps_lock); LIST_INIT(&pmaps); - TAILQ_INIT(&pv_freepages); - TAILQ_INIT(&pv_unusedpgs); /* * initialize the pmap pool. */ pool_init(&pmap_pmap_pool, sizeof(struct pmap), 0, 0, 0, "pmappl", - &pool_allocator_nointr); + &pool_allocator_nointr); + pool_init(&pmap_pv_pool, sizeof(struct pv_entry), 0, 0, 0, "pvpl", + &pool_allocator_nointr); /* * Initialize the TLB shootdown queues. @@ -1148,9 +922,7 @@ pmap_prealloc_lowmem_ptps(void) for (;;) { newp = avail_start; avail_start += PAGE_SIZE; - *early_zero_pte = (newp & PG_FRAME) | PG_V | PG_RW; - pmap_update_pg((vaddr_t)early_zerop); - memset(early_zerop, 0, PAGE_SIZE); + memset((void *)PMAP_DIRECT_MAP(newp), 0, PAGE_SIZE); pdes[pl_i(0, level)] = (newp & PG_FRAME) | PG_V | PG_RW; level--; if (level <= 1) @@ -1166,10 +938,11 @@ pmap_prealloc_lowmem_ptps(void) */ void -pmap_init() +pmap_init(void) { int lcv; unsigned int npages, i; + struct vm_page *pg; vaddr_t addr; vsize_t s; @@ -1230,24 +1003,10 @@ pmap_init() } #endif - /* - * now we need to free enough pv_entry structures to allow us to get - * the kmem_map allocated and inited (done after this - * function is finished). to do this we allocate one bootstrap page out - * of kernel_map and use it to provide an initial pool of pv_entry - * structures. we never free this page. - */ - - pv_initpage = (struct pv_page *) uvm_km_alloc(kernel_map, PAGE_SIZE); - if (pv_initpage == NULL) - panic("pmap_init: pv_initpage"); - pv_cachedva = 0; /* a VA we have allocated but not used yet */ - pv_nfpvents = 0; - (void) pmap_add_pvpage(pv_initpage, FALSE); - - pj_page = (void *)uvm_km_alloc(kernel_map, PAGE_SIZE); - if (pj_page == NULL) + pg = uvm_pagealloc(NULL, 0, NULL, UVM_PGA_USERESERVE); + if (pg == NULL) panic("pmap_init: pj_page"); + pj_page = (void *)pmap_map_direct(pg); for (i = 0; i < (PAGE_SIZE / sizeof (union pmap_tlb_shootdown_job_al) - 1); @@ -1268,340 +1027,6 @@ pmap_init() */ /* - * pv_entry allocation functions: - * the main pv_entry allocation functions are: - * pmap_alloc_pv: allocate a pv_entry structure - * pmap_free_pv: free one pv_entry - * pmap_free_pvs: free a list of pv_entrys - * - * the rest are helper functions - */ - -/* - * pmap_alloc_pv: inline function to allocate a pv_entry structure - * => we lock pvalloc_lock - * => if we fail, we call out to pmap_alloc_pvpage - * => 3 modes: - * ALLOCPV_NEED = we really need a pv_entry, even if we have to steal it - * ALLOCPV_TRY = we want a pv_entry, but not enough to steal - * ALLOCPV_NONEED = we are trying to grow our free list, don't really need - * one now - * - * "try" is for optional functions like pmap_copy(). - */ - -__inline static struct pv_entry * -pmap_alloc_pv(pmap, mode) - struct pmap *pmap; - int mode; -{ - struct pv_page *pvpage; - struct pv_entry *pv; - - simple_lock(&pvalloc_lock); - - pvpage = TAILQ_FIRST(&pv_freepages); - if (pvpage != NULL) { - pvpage->pvinfo.pvpi_nfree--; - if (pvpage->pvinfo.pvpi_nfree == 0) { - /* nothing left in this one? */ - TAILQ_REMOVE(&pv_freepages, pvpage, pvinfo.pvpi_list); - } - pv = pvpage->pvinfo.pvpi_pvfree; - KASSERT(pv); - pvpage->pvinfo.pvpi_pvfree = pv->pv_next; - pv_nfpvents--; /* took one from pool */ - } else { - pv = NULL; /* need more of them */ - } - - /* - * if below low water mark or we didn't get a pv_entry we try and - * create more pv_entrys ... - */ - - if (pv_nfpvents < PVE_LOWAT || pv == NULL) { - if (pv == NULL) - pv = pmap_alloc_pvpage(pmap, (mode == ALLOCPV_TRY) ? - mode : ALLOCPV_NEED); - else - (void) pmap_alloc_pvpage(pmap, ALLOCPV_NONEED); - } - - simple_unlock(&pvalloc_lock); - return(pv); -} - -/* - * pmap_alloc_pvpage: maybe allocate a new pvpage - * - * if need_entry is false: try and allocate a new pv_page - * if need_entry is true: try and allocate a new pv_page and return a - * new pv_entry from it. if we are unable to allocate a pv_page - * we make a last ditch effort to steal a pv_page from some other - * mapping. if that fails, we panic... - * - * => we assume that the caller holds pvalloc_lock - */ - -struct pv_entry * -pmap_alloc_pvpage(pmap, mode) - struct pmap *pmap; - int mode; -{ - struct vm_page *pg; - struct pv_page *pvpage; - struct pv_entry *pv; - int s; - - /* - * if we need_entry and we've got unused pv_pages, allocate from there - */ - - pvpage = TAILQ_FIRST(&pv_unusedpgs); - if (mode != ALLOCPV_NONEED && pvpage != NULL) { - - /* move it to pv_freepages list */ - TAILQ_REMOVE(&pv_unusedpgs, pvpage, pvinfo.pvpi_list); - TAILQ_INSERT_HEAD(&pv_freepages, pvpage, pvinfo.pvpi_list); - - /* allocate a pv_entry */ - pvpage->pvinfo.pvpi_nfree--; /* can't go to zero */ - pv = pvpage->pvinfo.pvpi_pvfree; - KASSERT(pv); - pvpage->pvinfo.pvpi_pvfree = pv->pv_next; - pv_nfpvents--; /* took one from pool */ - return(pv); - } - - /* - * see if we've got a cached unmapped VA that we can map a page in. - * if not, try to allocate one. - */ - - s = splvm(); /* must protect kmem_map with splvm! */ - if (pv_cachedva == 0) { - pv_cachedva = uvm_km_kmemalloc(kmem_map, uvmexp.kmem_object, - PAGE_SIZE, UVM_KMF_TRYLOCK|UVM_KMF_VALLOC); - if (pv_cachedva == 0) { - splx(s); - return (NULL); - } - } - - /* - * we have a VA, now let's try and allocate a page. - */ - if (!simple_lock_try(&uvmexp.kmem_object->vmobjlock)) { - splx(s); - return (NULL); - } - - pg = uvm_pagealloc(uvmexp.kmem_object, pv_cachedva - - vm_map_min(kernel_map), NULL, UVM_PGA_USERESERVE); - if (pg) - pg->flags &= ~PG_BUSY; /* never busy */ - - simple_unlock(&uvmexp.kmem_object->vmobjlock); - splx(s); - - if (pg == NULL) - return (NULL); - - /* - * add a mapping for our new pv_page and free its entrys (save one!) - * - * NOTE: If we are allocating a PV page for the kernel pmap, the - * pmap is already locked! (...but entering the mapping is safe...) - */ - - pmap_kenter_pa(pv_cachedva, VM_PAGE_TO_PHYS(pg), - VM_PROT_READ | VM_PROT_WRITE); - pmap_update(pmap_kernel()); - pvpage = (struct pv_page *) pv_cachedva; - pv_cachedva = 0; - return (pmap_add_pvpage(pvpage, mode != ALLOCPV_NONEED)); -} - -/* - * pmap_add_pvpage: add a pv_page's pv_entrys to the free list - * - * => caller must hold pvalloc_lock - * => if need_entry is true, we allocate and return one pv_entry - */ - -static struct pv_entry * -pmap_add_pvpage(pvp, need_entry) - struct pv_page *pvp; - boolean_t need_entry; -{ - int tofree, lcv; - - /* do we need to return one? */ - tofree = (need_entry) ? PVE_PER_PVPAGE - 1 : PVE_PER_PVPAGE; - - pvp->pvinfo.pvpi_pvfree = NULL; - pvp->pvinfo.pvpi_nfree = tofree; - for (lcv = 0 ; lcv < tofree ; lcv++) { - pvp->pvents[lcv].pv_next = pvp->pvinfo.pvpi_pvfree; - pvp->pvinfo.pvpi_pvfree = &pvp->pvents[lcv]; - } - if (need_entry) - TAILQ_INSERT_TAIL(&pv_freepages, pvp, pvinfo.pvpi_list); - else - TAILQ_INSERT_TAIL(&pv_unusedpgs, pvp, pvinfo.pvpi_list); - pv_nfpvents += tofree; - return((need_entry) ? &pvp->pvents[lcv] : NULL); -} - -/* - * pmap_free_pv_doit: actually free a pv_entry - * - * => do not call this directly! instead use either - * 1. pmap_free_pv ==> free a single pv_entry - * 2. pmap_free_pvs => free a list of pv_entrys - * => we must be holding pvalloc_lock - */ - -__inline static void -pmap_free_pv_doit(pv) - struct pv_entry *pv; -{ - struct pv_page *pvp; - - pvp = (struct pv_page *) x86_trunc_page(pv); - pv_nfpvents++; - pvp->pvinfo.pvpi_nfree++; - - /* nfree == 1 => fully allocated page just became partly allocated */ - if (pvp->pvinfo.pvpi_nfree == 1) { - TAILQ_INSERT_HEAD(&pv_freepages, pvp, pvinfo.pvpi_list); - } - - /* free it */ - pv->pv_next = pvp->pvinfo.pvpi_pvfree; - pvp->pvinfo.pvpi_pvfree = pv; - - /* - * are all pv_page's pv_entry's free? move it to unused queue. - */ - - if (pvp->pvinfo.pvpi_nfree == PVE_PER_PVPAGE) { - TAILQ_REMOVE(&pv_freepages, pvp, pvinfo.pvpi_list); - TAILQ_INSERT_HEAD(&pv_unusedpgs, pvp, pvinfo.pvpi_list); - } -} - -/* - * pmap_free_pv: free a single pv_entry - * - * => we gain the pvalloc_lock - */ - -__inline static void -pmap_free_pv(pmap, pv) - struct pmap *pmap; - struct pv_entry *pv; -{ - simple_lock(&pvalloc_lock); - pmap_free_pv_doit(pv); - - /* - * Can't free the PV page if the PV entries were associated with - * the kernel pmap; the pmap is already locked. - */ - if (pv_nfpvents > PVE_HIWAT && TAILQ_FIRST(&pv_unusedpgs) != NULL && - pmap != pmap_kernel()) - pmap_free_pvpage(); - - simple_unlock(&pvalloc_lock); -} - -/* - * pmap_free_pvs: free a list of pv_entrys - * - * => we gain the pvalloc_lock - */ - -__inline static void -pmap_free_pvs(pmap, pvs) - struct pmap *pmap; - struct pv_entry *pvs; -{ - struct pv_entry *nextpv; - - simple_lock(&pvalloc_lock); - - for ( /* null */ ; pvs != NULL ; pvs = nextpv) { - nextpv = pvs->pv_next; - pmap_free_pv_doit(pvs); - } - - /* - * Can't free the PV page if the PV entries were associated with - * the kernel pmap; the pmap is already locked. - */ - if (pv_nfpvents > PVE_HIWAT && TAILQ_FIRST(&pv_unusedpgs) != NULL && - pmap != pmap_kernel()) - pmap_free_pvpage(); - - simple_unlock(&pvalloc_lock); -} - - -/* - * pmap_free_pvpage: try and free an unused pv_page structure - * - * => assume caller is holding the pvalloc_lock and that - * there is a page on the pv_unusedpgs list - * => if we can't get a lock on the kmem_map we try again later - */ - -void -pmap_free_pvpage() -{ - int s; - struct vm_map *map; - struct vm_map_entry *dead_entries; - struct pv_page *pvp; - - s = splvm(); /* protect kmem_map */ - - pvp = TAILQ_FIRST(&pv_unusedpgs); - - /* - * note: watch out for pv_initpage which is allocated out of - * kernel_map rather than kmem_map. - */ - - if (pvp == pv_initpage) - map = kernel_map; - else - map = kmem_map; - if (vm_map_lock_try(map)) { - - /* remove pvp from pv_unusedpgs */ - TAILQ_REMOVE(&pv_unusedpgs, pvp, pvinfo.pvpi_list); - - /* unmap the page */ - dead_entries = NULL; - uvm_unmap_remove(map, (vaddr_t)pvp, ((vaddr_t)pvp) + PAGE_SIZE, - &dead_entries, NULL); - vm_map_unlock(map); - - if (dead_entries != NULL) - uvm_unmap_detach(dead_entries, 0); - - pv_nfpvents -= PVE_PER_PVPAGE; /* update free count */ - } - if (pvp == pv_initpage) - /* no more initpage, we've freed it */ - pv_initpage = NULL; - - splx(s); -} - -/* * main pv_entry manipulation functions: * pmap_enter_pv: enter a mapping onto a pv_head list * pmap_remove_pv: remove a mappiing from a pv_head list @@ -1619,7 +1044,7 @@ pmap_free_pvpage() * => caller should adjust ptp's wire_count before calling */ -__inline static void +void pmap_enter_pv(pvh, pve, pmap, va, ptp) struct pv_head *pvh; struct pv_entry *pve; /* preallocated pve for us to use */ @@ -1646,7 +1071,7 @@ pmap_enter_pv(pvh, pve, pmap, va, ptp) * => we return the removed pve */ -__inline static struct pv_entry * +struct pv_entry * pmap_remove_pv(pvh, pmap, va) struct pv_head *pvh; struct pmap *pmap; @@ -1671,7 +1096,7 @@ pmap_remove_pv(pvh, pmap, va) * p t p f u n c t i o n s */ -static __inline struct vm_page * +struct vm_page * pmap_find_ptp(struct pmap *pmap, vaddr_t va, paddr_t pa, int level) { int lidx = level - 1; @@ -1691,7 +1116,7 @@ pmap_find_ptp(struct pmap *pmap, vaddr_t va, paddr_t pa, int level) return pg; } -static __inline void +void pmap_freepage(struct pmap *pmap, struct vm_page *ptp, int level) { int lidx; @@ -2272,26 +1697,7 @@ pmap_map(va, spa, epa, prot) void pmap_zero_page(struct vm_page *pg) { - paddr_t pa = VM_PAGE_TO_PHYS(pg); - -#ifdef MULTIPROCESSOR - int id = cpu_number(); -#endif - pt_entry_t *zpte = PTESLEW(zero_pte, id); - caddr_t zerova = VASLEW(zerop, id); - -#ifdef DIAGNOSTIC - if (*zpte) - panic("pmap_zero_page: lock botch"); -#endif - - *zpte = (pa & PG_FRAME) | PG_V | PG_RW; /* map in */ - pmap_update_pg((vaddr_t)zerova); /* flush TLB */ - - memset(zerova, 0, PAGE_SIZE); /* zero */ -#ifdef DIAGNOSTIC - *zpte = 0; /* zap! */ -#endif + pagezero(pmap_map_direct(pg)); } /* @@ -2303,23 +1709,18 @@ pmap_zero_page(struct vm_page *pg) boolean_t pmap_pageidlezero(struct vm_page *pg) { - paddr_t pa = VM_PAGE_TO_PHYS(pg); -#ifdef MULTIPROCESSOR - int id = cpu_number(); -#endif - pt_entry_t *zpte = PTESLEW(zero_pte, id); - caddr_t zerova = VASLEW(zerop, id); + vaddr_t va = pmap_map_direct(pg); boolean_t rv = TRUE; - int *ptr; - unsigned int i; + long *ptr; + int i; -#ifdef DIAGNOSTIC - if (*zpte) - panic("pmap_zero_page_uncached: lock botch"); -#endif - *zpte = (pa & PG_FRAME) | PG_V | PG_RW | PG_N; /* map in */ - pmap_update_pg((vaddr_t)zerova); /* flush TLB */ - for (i = 0, ptr = (int *) zerova; i < PAGE_SIZE / sizeof(int); i++) { + /* + * XXX - We'd really like to do this uncached. But at this moment + * we're never called, so just pretend that this works. + * It shouldn't be too hard to create a second direct map + * with uncached mappings. + */ + for (i = 0, ptr = (long *) va; i < PAGE_SIZE / sizeof(long); i++) { if (whichqs != 0) { /* @@ -2335,9 +1736,6 @@ pmap_pageidlezero(struct vm_page *pg) *ptr++ = 0; } -#ifdef DIAGNOSTIC - *zpte = 0; /* zap! */ -#endif return (rv); } @@ -2348,28 +1746,10 @@ pmap_pageidlezero(struct vm_page *pg) void pmap_copy_page(struct vm_page *srcpg, struct vm_page *dstpg) { - paddr_t srcpa = VM_PAGE_TO_PHYS(srcpg); - paddr_t dstpa = VM_PAGE_TO_PHYS(dstpg); -#ifdef MULTIPROCESSOR - int id = cpu_number(); -#endif - pt_entry_t *spte = PTESLEW(csrc_pte,id); - pt_entry_t *dpte = PTESLEW(cdst_pte,id); - caddr_t csrcva = VASLEW(csrcp, id); - caddr_t cdstva = VASLEW(cdstp, id); - -#ifdef DIAGNOSTIC - if (*spte || *dpte) - panic("pmap_copy_page: lock botch"); -#endif + vaddr_t srcva = pmap_map_direct(srcpg); + vaddr_t dstva = pmap_map_direct(dstpg); - *spte = (srcpa & PG_FRAME) | PG_V | PG_RW; - *dpte = (dstpa & PG_FRAME) | PG_V | PG_RW; - pmap_update_2pg((vaddr_t)csrcva, (vaddr_t)cdstva); - memcpy(cdstva, csrcva, PAGE_SIZE); -#ifdef DIAGNOSTIC - *spte = *dpte = 0; /* zap! */ -#endif + memcpy((void *)dstva, (void *)srcva, PAGE_SIZE); } /* @@ -2396,7 +1776,6 @@ pmap_remove_ptes(pmap, ptp, ptpva, startva, endva, cpumaskp, flags) int32_t *cpumaskp; int flags; { - struct pv_entry *pv_tofree = NULL; /* list of pv_entrys to free */ struct pv_entry *pve; pt_entry_t *pte = (pt_entry_t *) ptpva; pt_entry_t opte; @@ -2461,14 +1840,11 @@ pmap_remove_ptes(pmap, ptp, ptpva, startva, endva, cpumaskp, flags) simple_unlock(&vm_physmem[bank].pmseg.pvhead[off].pvh_lock); if (pve) { - pve->pv_next = pv_tofree; - pv_tofree = pve; + pool_put(&pmap_pv_pool, pve); } /* end of "for" loop: time for next pte */ } - if (pv_tofree) - pmap_free_pvs(pmap, pv_tofree); } @@ -2542,7 +1918,7 @@ pmap_remove_pte(pmap, ptp, pte, va, cpumaskp, flags) simple_unlock(&vm_physmem[bank].pmseg.pvhead[off].pvh_lock); if (pve) - pmap_free_pv(pmap, pve); + pool_put(&pmap_pv_pool, pve); return(TRUE); } @@ -2704,7 +2080,7 @@ pmap_page_remove(pg) { int bank, off; struct pv_head *pvh; - struct pv_entry *pve, *npve, **prevptr, *killlist = NULL; + struct pv_entry *pve, *npve, **prevptr; pt_entry_t *ptes, opte; pd_entry_t **pdes; #ifdef DIAGNOSTIC @@ -2771,10 +2147,9 @@ pmap_page_remove(pg) } pmap_unmap_ptes(pve->pv_pmap); /* unlocks pmap */ *prevptr = npve; /* remove it */ - pve->pv_next = killlist; /* mark it for death */ - killlist = pve; + pool_put(&pmap_pv_pool, pve); } - pmap_free_pvs(NULL, killlist); + pvh->pvh_list = NULL; simple_unlock(&pvh->pvh_lock); PMAP_HEAD_TO_MAP_UNLOCK(); @@ -3242,7 +2617,7 @@ pmap_enter(pmap, va, pa, prot, flags) if (pmap_initialized && bank != -1) { pvh = &vm_physmem[bank].pmseg.pvhead[off]; if (pve == NULL) { - pve = pmap_alloc_pv(pmap, ALLOCPV_NEED); + pve = pool_get(&pmap_pv_pool, PR_NOWAIT); if (pve == NULL) { if (flags & PMAP_CANFAIL) { error = ENOMEM; @@ -3258,7 +2633,7 @@ pmap_enter(pmap, va, pa, prot, flags) /* new mapping is not PG_PVLIST. free pve if we've got one */ pvh = NULL; /* ensure !PG_PVLIST */ if (pve) - pmap_free_pv(pmap, pve); + pool_put(&pmap_pv_pool, pve); } enter_now: @@ -3328,9 +2703,7 @@ pmap_get_physpage(va, level, paddrp) if (uvm_page_physget(paddrp) == FALSE) panic("pmap_get_physpage: out of memory"); - *early_zero_pte = (*paddrp & PG_FRAME) | PG_V | PG_RW; - pmap_update_pg((vaddr_t)early_zerop); - memset(early_zerop, 0, PAGE_SIZE); + memset((void *)PMAP_DIRECT_MAP(*paddrp), 0, PAGE_SIZE); } else { ptp = uvm_pagealloc(&kpm->pm_obj[level - 1], ptp_va2o(va, level), NULL, |