111752d88SAlan Cox /*- 211752d88SAlan Cox * Copyright (c) 2002-2006 Rice University 311752d88SAlan Cox * Copyright (c) 2007 Alan L. Cox <alc@cs.rice.edu> 411752d88SAlan Cox * All rights reserved. 511752d88SAlan Cox * 611752d88SAlan Cox * This software was developed for the FreeBSD Project by Alan L. Cox, 711752d88SAlan Cox * Olivier Crameri, Peter Druschel, Sitaram Iyer, and Juan Navarro. 811752d88SAlan Cox * 911752d88SAlan Cox * Redistribution and use in source and binary forms, with or without 1011752d88SAlan Cox * modification, are permitted provided that the following conditions 1111752d88SAlan Cox * are met: 1211752d88SAlan Cox * 1. Redistributions of source code must retain the above copyright 1311752d88SAlan Cox * notice, this list of conditions and the following disclaimer. 1411752d88SAlan Cox * 2. Redistributions in binary form must reproduce the above copyright 1511752d88SAlan Cox * notice, this list of conditions and the following disclaimer in the 1611752d88SAlan Cox * documentation and/or other materials provided with the distribution. 1711752d88SAlan Cox * 1811752d88SAlan Cox * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1911752d88SAlan Cox * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2011752d88SAlan Cox * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2111752d88SAlan Cox * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2211752d88SAlan Cox * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2311752d88SAlan Cox * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2411752d88SAlan Cox * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 2511752d88SAlan Cox * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2611752d88SAlan Cox * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2711752d88SAlan Cox * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 2811752d88SAlan Cox * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2911752d88SAlan Cox * POSSIBILITY OF SUCH DAMAGE. 3011752d88SAlan Cox */ 3111752d88SAlan Cox 32fbd80bd0SAlan Cox /* 33fbd80bd0SAlan Cox * Physical memory system implementation 34fbd80bd0SAlan Cox * 35fbd80bd0SAlan Cox * Any external functions defined by this module are only to be used by the 36fbd80bd0SAlan Cox * virtual memory system. 37fbd80bd0SAlan Cox */ 38fbd80bd0SAlan Cox 3911752d88SAlan Cox #include <sys/cdefs.h> 4011752d88SAlan Cox __FBSDID("$FreeBSD$"); 4111752d88SAlan Cox 4211752d88SAlan Cox #include "opt_ddb.h" 43174b5f38SJohn Baldwin #include "opt_vm.h" 4411752d88SAlan Cox 4511752d88SAlan Cox #include <sys/param.h> 4611752d88SAlan Cox #include <sys/systm.h> 4711752d88SAlan Cox #include <sys/lock.h> 4811752d88SAlan Cox #include <sys/kernel.h> 4911752d88SAlan Cox #include <sys/malloc.h> 5011752d88SAlan Cox #include <sys/mutex.h> 517e226537SAttilio Rao #include <sys/proc.h> 5211752d88SAlan Cox #include <sys/queue.h> 5338d6b2dcSRoger Pau Monné #include <sys/rwlock.h> 5411752d88SAlan Cox #include <sys/sbuf.h> 5511752d88SAlan Cox #include <sys/sysctl.h> 5638d6b2dcSRoger Pau Monné #include <sys/tree.h> 5711752d88SAlan Cox #include <sys/vmmeter.h> 586520495aSAdrian Chadd #include <sys/seq.h> 5911752d88SAlan Cox 6011752d88SAlan Cox #include <ddb/ddb.h> 6111752d88SAlan Cox 6211752d88SAlan Cox #include <vm/vm.h> 6311752d88SAlan Cox #include <vm/vm_param.h> 6411752d88SAlan Cox #include <vm/vm_kern.h> 6511752d88SAlan Cox #include <vm/vm_object.h> 6611752d88SAlan Cox #include <vm/vm_page.h> 6711752d88SAlan Cox #include <vm/vm_phys.h> 6811752d88SAlan Cox 696520495aSAdrian Chadd #include <vm/vm_domain.h> 706520495aSAdrian Chadd 71449c2e92SKonstantin Belousov _Static_assert(sizeof(long) * NBBY >= VM_PHYSSEG_MAX, 72449c2e92SKonstantin Belousov "Too many physsegs."); 7311752d88SAlan Cox 74*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 75a3870a18SJohn Baldwin struct mem_affinity *mem_affinity; 76415d7ccaSAdrian Chadd int *mem_locality; 77*62d70a81SJohn Baldwin #endif 78a3870a18SJohn Baldwin 797e226537SAttilio Rao int vm_ndomains = 1; 807e226537SAttilio Rao 81449c2e92SKonstantin Belousov struct vm_phys_seg vm_phys_segs[VM_PHYSSEG_MAX]; 82449c2e92SKonstantin Belousov int vm_phys_nsegs; 8311752d88SAlan Cox 8438d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg; 8538d6b2dcSRoger Pau Monné static int vm_phys_fictitious_cmp(struct vm_phys_fictitious_seg *, 8638d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *); 8738d6b2dcSRoger Pau Monné 8838d6b2dcSRoger Pau Monné RB_HEAD(fict_tree, vm_phys_fictitious_seg) vm_phys_fictitious_tree = 8938d6b2dcSRoger Pau Monné RB_INITIALIZER(_vm_phys_fictitious_tree); 9038d6b2dcSRoger Pau Monné 9138d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg { 9238d6b2dcSRoger Pau Monné RB_ENTRY(vm_phys_fictitious_seg) node; 9338d6b2dcSRoger Pau Monné /* Memory region data */ 94b6de32bdSKonstantin Belousov vm_paddr_t start; 95b6de32bdSKonstantin Belousov vm_paddr_t end; 96b6de32bdSKonstantin Belousov vm_page_t first_page; 9738d6b2dcSRoger Pau Monné }; 9838d6b2dcSRoger Pau Monné 9938d6b2dcSRoger Pau Monné RB_GENERATE_STATIC(fict_tree, vm_phys_fictitious_seg, node, 10038d6b2dcSRoger Pau Monné vm_phys_fictitious_cmp); 10138d6b2dcSRoger Pau Monné 10238d6b2dcSRoger Pau Monné static struct rwlock vm_phys_fictitious_reg_lock; 103c0432fc3SMark Johnston MALLOC_DEFINE(M_FICT_PAGES, "vm_fictitious", "Fictitious VM pages"); 104b6de32bdSKonstantin Belousov 10511752d88SAlan Cox static struct vm_freelist 1067e226537SAttilio Rao vm_phys_free_queues[MAXMEMDOM][VM_NFREELIST][VM_NFREEPOOL][VM_NFREEORDER]; 10711752d88SAlan Cox 108d866a563SAlan Cox static int vm_nfreelists; 109d866a563SAlan Cox 110d866a563SAlan Cox /* 111d866a563SAlan Cox * Provides the mapping from VM_FREELIST_* to free list indices (flind). 112d866a563SAlan Cox */ 113d866a563SAlan Cox static int vm_freelist_to_flind[VM_NFREELIST]; 114d866a563SAlan Cox 115d866a563SAlan Cox CTASSERT(VM_FREELIST_DEFAULT == 0); 116d866a563SAlan Cox 117d866a563SAlan Cox #ifdef VM_FREELIST_ISADMA 118d866a563SAlan Cox #define VM_ISADMA_BOUNDARY 16777216 119d866a563SAlan Cox #endif 120d866a563SAlan Cox #ifdef VM_FREELIST_DMA32 121d866a563SAlan Cox #define VM_DMA32_BOUNDARY ((vm_paddr_t)1 << 32) 122d866a563SAlan Cox #endif 123d866a563SAlan Cox 124d866a563SAlan Cox /* 125d866a563SAlan Cox * Enforce the assumptions made by vm_phys_add_seg() and vm_phys_init() about 126d866a563SAlan Cox * the ordering of the free list boundaries. 127d866a563SAlan Cox */ 128d866a563SAlan Cox #if defined(VM_ISADMA_BOUNDARY) && defined(VM_LOWMEM_BOUNDARY) 129d866a563SAlan Cox CTASSERT(VM_ISADMA_BOUNDARY < VM_LOWMEM_BOUNDARY); 130d866a563SAlan Cox #endif 131d866a563SAlan Cox #if defined(VM_LOWMEM_BOUNDARY) && defined(VM_DMA32_BOUNDARY) 132d866a563SAlan Cox CTASSERT(VM_LOWMEM_BOUNDARY < VM_DMA32_BOUNDARY); 133d866a563SAlan Cox #endif 13411752d88SAlan Cox 13511752d88SAlan Cox static int cnt_prezero; 13611752d88SAlan Cox SYSCTL_INT(_vm_stats_misc, OID_AUTO, cnt_prezero, CTLFLAG_RD, 13711752d88SAlan Cox &cnt_prezero, 0, "The number of physical pages prezeroed at idle time"); 13811752d88SAlan Cox 13911752d88SAlan Cox static int sysctl_vm_phys_free(SYSCTL_HANDLER_ARGS); 14011752d88SAlan Cox SYSCTL_OID(_vm, OID_AUTO, phys_free, CTLTYPE_STRING | CTLFLAG_RD, 14111752d88SAlan Cox NULL, 0, sysctl_vm_phys_free, "A", "Phys Free Info"); 14211752d88SAlan Cox 14311752d88SAlan Cox static int sysctl_vm_phys_segs(SYSCTL_HANDLER_ARGS); 14411752d88SAlan Cox SYSCTL_OID(_vm, OID_AUTO, phys_segs, CTLTYPE_STRING | CTLFLAG_RD, 14511752d88SAlan Cox NULL, 0, sysctl_vm_phys_segs, "A", "Phys Seg Info"); 14611752d88SAlan Cox 147*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 148415d7ccaSAdrian Chadd static int sysctl_vm_phys_locality(SYSCTL_HANDLER_ARGS); 149415d7ccaSAdrian Chadd SYSCTL_OID(_vm, OID_AUTO, phys_locality, CTLTYPE_STRING | CTLFLAG_RD, 150415d7ccaSAdrian Chadd NULL, 0, sysctl_vm_phys_locality, "A", "Phys Locality Info"); 1516520495aSAdrian Chadd #endif 152415d7ccaSAdrian Chadd 1537e226537SAttilio Rao SYSCTL_INT(_vm, OID_AUTO, ndomains, CTLFLAG_RD, 1547e226537SAttilio Rao &vm_ndomains, 0, "Number of physical memory domains available."); 155a3870a18SJohn Baldwin 1566520495aSAdrian Chadd /* 1576520495aSAdrian Chadd * Default to first-touch + round-robin. 1586520495aSAdrian Chadd */ 1596520495aSAdrian Chadd static struct mtx vm_default_policy_mtx; 1606520495aSAdrian Chadd MTX_SYSINIT(vm_default_policy, &vm_default_policy_mtx, "default policy mutex", 1616520495aSAdrian Chadd MTX_DEF); 162*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 1636520495aSAdrian Chadd static struct vm_domain_policy vm_default_policy = 1646520495aSAdrian Chadd VM_DOMAIN_POLICY_STATIC_INITIALISER(VM_POLICY_FIRST_TOUCH_ROUND_ROBIN, 0); 1656520495aSAdrian Chadd #else 1666520495aSAdrian Chadd /* Use round-robin so the domain policy code will only try once per allocation */ 1676520495aSAdrian Chadd static struct vm_domain_policy vm_default_policy = 1686520495aSAdrian Chadd VM_DOMAIN_POLICY_STATIC_INITIALISER(VM_POLICY_ROUND_ROBIN, 0); 1696520495aSAdrian Chadd #endif 1706520495aSAdrian Chadd 171f5c4b077SJohn Baldwin static vm_page_t vm_phys_alloc_domain_pages(int domain, int flind, int pool, 172f5c4b077SJohn Baldwin int order); 173c869e672SAlan Cox static vm_page_t vm_phys_alloc_seg_contig(struct vm_phys_seg *seg, 174c869e672SAlan Cox u_long npages, vm_paddr_t low, vm_paddr_t high, u_long alignment, 175c869e672SAlan Cox vm_paddr_t boundary); 176d866a563SAlan Cox static void _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int domain); 177d866a563SAlan Cox static void vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end); 17811752d88SAlan Cox static int vm_phys_paddr_to_segind(vm_paddr_t pa); 17911752d88SAlan Cox static void vm_phys_split_pages(vm_page_t m, int oind, struct vm_freelist *fl, 18011752d88SAlan Cox int order); 18111752d88SAlan Cox 1826520495aSAdrian Chadd static int 1836520495aSAdrian Chadd sysctl_vm_default_policy(SYSCTL_HANDLER_ARGS) 1846520495aSAdrian Chadd { 1856520495aSAdrian Chadd char policy_name[32]; 1866520495aSAdrian Chadd int error; 1876520495aSAdrian Chadd 1886520495aSAdrian Chadd mtx_lock(&vm_default_policy_mtx); 1896520495aSAdrian Chadd 1906520495aSAdrian Chadd /* Map policy to output string */ 1916520495aSAdrian Chadd switch (vm_default_policy.p.policy) { 1926520495aSAdrian Chadd case VM_POLICY_FIRST_TOUCH: 1936520495aSAdrian Chadd strcpy(policy_name, "first-touch"); 1946520495aSAdrian Chadd break; 1956520495aSAdrian Chadd case VM_POLICY_FIRST_TOUCH_ROUND_ROBIN: 1966520495aSAdrian Chadd strcpy(policy_name, "first-touch-rr"); 1976520495aSAdrian Chadd break; 1986520495aSAdrian Chadd case VM_POLICY_ROUND_ROBIN: 1996520495aSAdrian Chadd default: 2006520495aSAdrian Chadd strcpy(policy_name, "rr"); 2016520495aSAdrian Chadd break; 2026520495aSAdrian Chadd } 2036520495aSAdrian Chadd mtx_unlock(&vm_default_policy_mtx); 2046520495aSAdrian Chadd 2056520495aSAdrian Chadd error = sysctl_handle_string(oidp, &policy_name[0], 2066520495aSAdrian Chadd sizeof(policy_name), req); 2076520495aSAdrian Chadd if (error != 0 || req->newptr == NULL) 2086520495aSAdrian Chadd return (error); 2096520495aSAdrian Chadd 2106520495aSAdrian Chadd mtx_lock(&vm_default_policy_mtx); 2116520495aSAdrian Chadd /* Set: match on the subset of policies that make sense as a default */ 2126520495aSAdrian Chadd if (strcmp("first-touch-rr", policy_name) == 0) { 2136520495aSAdrian Chadd vm_domain_policy_set(&vm_default_policy, 2146520495aSAdrian Chadd VM_POLICY_FIRST_TOUCH_ROUND_ROBIN, 0); 2156520495aSAdrian Chadd } else if (strcmp("first-touch", policy_name) == 0) { 2166520495aSAdrian Chadd vm_domain_policy_set(&vm_default_policy, 2176520495aSAdrian Chadd VM_POLICY_FIRST_TOUCH, 0); 2186520495aSAdrian Chadd } else if (strcmp("rr", policy_name) == 0) { 2196520495aSAdrian Chadd vm_domain_policy_set(&vm_default_policy, 2206520495aSAdrian Chadd VM_POLICY_ROUND_ROBIN, 0); 2216520495aSAdrian Chadd } else { 2226520495aSAdrian Chadd error = EINVAL; 2236520495aSAdrian Chadd goto finish; 2246520495aSAdrian Chadd } 2256520495aSAdrian Chadd 2266520495aSAdrian Chadd error = 0; 2276520495aSAdrian Chadd finish: 2286520495aSAdrian Chadd mtx_unlock(&vm_default_policy_mtx); 2296520495aSAdrian Chadd return (error); 2306520495aSAdrian Chadd } 2316520495aSAdrian Chadd 2326520495aSAdrian Chadd SYSCTL_PROC(_vm, OID_AUTO, default_policy, CTLTYPE_STRING | CTLFLAG_RW, 2336520495aSAdrian Chadd 0, 0, sysctl_vm_default_policy, "A", 2346520495aSAdrian Chadd "Default policy (rr, first-touch, first-touch-rr"); 2356520495aSAdrian Chadd 23638d6b2dcSRoger Pau Monné /* 23738d6b2dcSRoger Pau Monné * Red-black tree helpers for vm fictitious range management. 23838d6b2dcSRoger Pau Monné */ 23938d6b2dcSRoger Pau Monné static inline int 24038d6b2dcSRoger Pau Monné vm_phys_fictitious_in_range(struct vm_phys_fictitious_seg *p, 24138d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *range) 24238d6b2dcSRoger Pau Monné { 24338d6b2dcSRoger Pau Monné 24438d6b2dcSRoger Pau Monné KASSERT(range->start != 0 && range->end != 0, 24538d6b2dcSRoger Pau Monné ("Invalid range passed on search for vm_fictitious page")); 24638d6b2dcSRoger Pau Monné if (p->start >= range->end) 24738d6b2dcSRoger Pau Monné return (1); 24838d6b2dcSRoger Pau Monné if (p->start < range->start) 24938d6b2dcSRoger Pau Monné return (-1); 25038d6b2dcSRoger Pau Monné 25138d6b2dcSRoger Pau Monné return (0); 25238d6b2dcSRoger Pau Monné } 25338d6b2dcSRoger Pau Monné 25438d6b2dcSRoger Pau Monné static int 25538d6b2dcSRoger Pau Monné vm_phys_fictitious_cmp(struct vm_phys_fictitious_seg *p1, 25638d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *p2) 25738d6b2dcSRoger Pau Monné { 25838d6b2dcSRoger Pau Monné 25938d6b2dcSRoger Pau Monné /* Check if this is a search for a page */ 26038d6b2dcSRoger Pau Monné if (p1->end == 0) 26138d6b2dcSRoger Pau Monné return (vm_phys_fictitious_in_range(p1, p2)); 26238d6b2dcSRoger Pau Monné 26338d6b2dcSRoger Pau Monné KASSERT(p2->end != 0, 26438d6b2dcSRoger Pau Monné ("Invalid range passed as second parameter to vm fictitious comparison")); 26538d6b2dcSRoger Pau Monné 26638d6b2dcSRoger Pau Monné /* Searching to add a new range */ 26738d6b2dcSRoger Pau Monné if (p1->end <= p2->start) 26838d6b2dcSRoger Pau Monné return (-1); 26938d6b2dcSRoger Pau Monné if (p1->start >= p2->end) 27038d6b2dcSRoger Pau Monné return (1); 27138d6b2dcSRoger Pau Monné 27238d6b2dcSRoger Pau Monné panic("Trying to add overlapping vm fictitious ranges:\n" 27338d6b2dcSRoger Pau Monné "[%#jx:%#jx] and [%#jx:%#jx]", (uintmax_t)p1->start, 27438d6b2dcSRoger Pau Monné (uintmax_t)p1->end, (uintmax_t)p2->start, (uintmax_t)p2->end); 27538d6b2dcSRoger Pau Monné } 27638d6b2dcSRoger Pau Monné 2777e226537SAttilio Rao static __inline int 2787e226537SAttilio Rao vm_rr_selectdomain(void) 2797e226537SAttilio Rao { 280*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 2817e226537SAttilio Rao struct thread *td; 2827e226537SAttilio Rao 2837e226537SAttilio Rao td = curthread; 2847e226537SAttilio Rao 2857e226537SAttilio Rao td->td_dom_rr_idx++; 2867e226537SAttilio Rao td->td_dom_rr_idx %= vm_ndomains; 2877e226537SAttilio Rao return (td->td_dom_rr_idx); 2887e226537SAttilio Rao #else 2897e226537SAttilio Rao return (0); 2907e226537SAttilio Rao #endif 2917e226537SAttilio Rao } 2927e226537SAttilio Rao 2936520495aSAdrian Chadd /* 2946520495aSAdrian Chadd * Initialise a VM domain iterator. 2956520495aSAdrian Chadd * 2966520495aSAdrian Chadd * Check the thread policy, then the proc policy, 2976520495aSAdrian Chadd * then default to the system policy. 2986520495aSAdrian Chadd * 2996520495aSAdrian Chadd * Later on the various layers will have this logic 3006520495aSAdrian Chadd * plumbed into them and the phys code will be explicitly 3016520495aSAdrian Chadd * handed a VM domain policy to use. 3026520495aSAdrian Chadd */ 3036520495aSAdrian Chadd static void 3046520495aSAdrian Chadd vm_policy_iterator_init(struct vm_domain_iterator *vi) 3056520495aSAdrian Chadd { 306*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 3076520495aSAdrian Chadd struct vm_domain_policy lcl; 3086520495aSAdrian Chadd #endif 3096520495aSAdrian Chadd 3106520495aSAdrian Chadd vm_domain_iterator_init(vi); 3116520495aSAdrian Chadd 312*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 3136520495aSAdrian Chadd /* Copy out the thread policy */ 3146520495aSAdrian Chadd vm_domain_policy_localcopy(&lcl, &curthread->td_vm_dom_policy); 3156520495aSAdrian Chadd if (lcl.p.policy != VM_POLICY_NONE) { 3166520495aSAdrian Chadd /* Thread policy is present; use it */ 3176520495aSAdrian Chadd vm_domain_iterator_set_policy(vi, &lcl); 3186520495aSAdrian Chadd return; 3196520495aSAdrian Chadd } 3206520495aSAdrian Chadd 3216520495aSAdrian Chadd vm_domain_policy_localcopy(&lcl, 3226520495aSAdrian Chadd &curthread->td_proc->p_vm_dom_policy); 3236520495aSAdrian Chadd if (lcl.p.policy != VM_POLICY_NONE) { 3246520495aSAdrian Chadd /* Process policy is present; use it */ 3256520495aSAdrian Chadd vm_domain_iterator_set_policy(vi, &lcl); 3266520495aSAdrian Chadd return; 3276520495aSAdrian Chadd } 3286520495aSAdrian Chadd #endif 3296520495aSAdrian Chadd /* Use system default policy */ 3306520495aSAdrian Chadd vm_domain_iterator_set_policy(vi, &vm_default_policy); 3316520495aSAdrian Chadd } 3326520495aSAdrian Chadd 3336520495aSAdrian Chadd static void 3346520495aSAdrian Chadd vm_policy_iterator_finish(struct vm_domain_iterator *vi) 3356520495aSAdrian Chadd { 3366520495aSAdrian Chadd 3376520495aSAdrian Chadd vm_domain_iterator_cleanup(vi); 3386520495aSAdrian Chadd } 3396520495aSAdrian Chadd 340449c2e92SKonstantin Belousov boolean_t 341449c2e92SKonstantin Belousov vm_phys_domain_intersects(long mask, vm_paddr_t low, vm_paddr_t high) 342449c2e92SKonstantin Belousov { 343449c2e92SKonstantin Belousov struct vm_phys_seg *s; 344449c2e92SKonstantin Belousov int idx; 345449c2e92SKonstantin Belousov 346449c2e92SKonstantin Belousov while ((idx = ffsl(mask)) != 0) { 347449c2e92SKonstantin Belousov idx--; /* ffsl counts from 1 */ 348449c2e92SKonstantin Belousov mask &= ~(1UL << idx); 349449c2e92SKonstantin Belousov s = &vm_phys_segs[idx]; 350449c2e92SKonstantin Belousov if (low < s->end && high > s->start) 351449c2e92SKonstantin Belousov return (TRUE); 352449c2e92SKonstantin Belousov } 353449c2e92SKonstantin Belousov return (FALSE); 354449c2e92SKonstantin Belousov } 355449c2e92SKonstantin Belousov 35611752d88SAlan Cox /* 35711752d88SAlan Cox * Outputs the state of the physical memory allocator, specifically, 35811752d88SAlan Cox * the amount of physical memory in each free list. 35911752d88SAlan Cox */ 36011752d88SAlan Cox static int 36111752d88SAlan Cox sysctl_vm_phys_free(SYSCTL_HANDLER_ARGS) 36211752d88SAlan Cox { 36311752d88SAlan Cox struct sbuf sbuf; 36411752d88SAlan Cox struct vm_freelist *fl; 3657e226537SAttilio Rao int dom, error, flind, oind, pind; 36611752d88SAlan Cox 36700f0e671SMatthew D Fleming error = sysctl_wire_old_buffer(req, 0); 36800f0e671SMatthew D Fleming if (error != 0) 36900f0e671SMatthew D Fleming return (error); 3707e226537SAttilio Rao sbuf_new_for_sysctl(&sbuf, NULL, 128 * vm_ndomains, req); 3717e226537SAttilio Rao for (dom = 0; dom < vm_ndomains; dom++) { 372eb2f42fbSAlan Cox sbuf_printf(&sbuf,"\nDOMAIN %d:\n", dom); 37311752d88SAlan Cox for (flind = 0; flind < vm_nfreelists; flind++) { 374eb2f42fbSAlan Cox sbuf_printf(&sbuf, "\nFREE LIST %d:\n" 37511752d88SAlan Cox "\n ORDER (SIZE) | NUMBER" 37611752d88SAlan Cox "\n ", flind); 37711752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 37811752d88SAlan Cox sbuf_printf(&sbuf, " | POOL %d", pind); 37911752d88SAlan Cox sbuf_printf(&sbuf, "\n-- "); 38011752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 38111752d88SAlan Cox sbuf_printf(&sbuf, "-- -- "); 38211752d88SAlan Cox sbuf_printf(&sbuf, "--\n"); 38311752d88SAlan Cox for (oind = VM_NFREEORDER - 1; oind >= 0; oind--) { 384d689bc00SAlan Cox sbuf_printf(&sbuf, " %2d (%6dK)", oind, 38511752d88SAlan Cox 1 << (PAGE_SHIFT - 10 + oind)); 38611752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 3877e226537SAttilio Rao fl = vm_phys_free_queues[dom][flind][pind]; 388eb2f42fbSAlan Cox sbuf_printf(&sbuf, " | %6d", 3897e226537SAttilio Rao fl[oind].lcnt); 39011752d88SAlan Cox } 39111752d88SAlan Cox sbuf_printf(&sbuf, "\n"); 39211752d88SAlan Cox } 3937e226537SAttilio Rao } 39411752d88SAlan Cox } 3954e657159SMatthew D Fleming error = sbuf_finish(&sbuf); 39611752d88SAlan Cox sbuf_delete(&sbuf); 39711752d88SAlan Cox return (error); 39811752d88SAlan Cox } 39911752d88SAlan Cox 40011752d88SAlan Cox /* 40111752d88SAlan Cox * Outputs the set of physical memory segments. 40211752d88SAlan Cox */ 40311752d88SAlan Cox static int 40411752d88SAlan Cox sysctl_vm_phys_segs(SYSCTL_HANDLER_ARGS) 40511752d88SAlan Cox { 40611752d88SAlan Cox struct sbuf sbuf; 40711752d88SAlan Cox struct vm_phys_seg *seg; 40811752d88SAlan Cox int error, segind; 40911752d88SAlan Cox 41000f0e671SMatthew D Fleming error = sysctl_wire_old_buffer(req, 0); 41100f0e671SMatthew D Fleming if (error != 0) 41200f0e671SMatthew D Fleming return (error); 4134e657159SMatthew D Fleming sbuf_new_for_sysctl(&sbuf, NULL, 128, req); 41411752d88SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 41511752d88SAlan Cox sbuf_printf(&sbuf, "\nSEGMENT %d:\n\n", segind); 41611752d88SAlan Cox seg = &vm_phys_segs[segind]; 41711752d88SAlan Cox sbuf_printf(&sbuf, "start: %#jx\n", 41811752d88SAlan Cox (uintmax_t)seg->start); 41911752d88SAlan Cox sbuf_printf(&sbuf, "end: %#jx\n", 42011752d88SAlan Cox (uintmax_t)seg->end); 421a3870a18SJohn Baldwin sbuf_printf(&sbuf, "domain: %d\n", seg->domain); 42211752d88SAlan Cox sbuf_printf(&sbuf, "free list: %p\n", seg->free_queues); 42311752d88SAlan Cox } 4244e657159SMatthew D Fleming error = sbuf_finish(&sbuf); 42511752d88SAlan Cox sbuf_delete(&sbuf); 42611752d88SAlan Cox return (error); 42711752d88SAlan Cox } 42811752d88SAlan Cox 429415d7ccaSAdrian Chadd /* 430415d7ccaSAdrian Chadd * Return affinity, or -1 if there's no affinity information. 431415d7ccaSAdrian Chadd */ 4326520495aSAdrian Chadd int 433415d7ccaSAdrian Chadd vm_phys_mem_affinity(int f, int t) 434415d7ccaSAdrian Chadd { 435415d7ccaSAdrian Chadd 436*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 437415d7ccaSAdrian Chadd if (mem_locality == NULL) 438415d7ccaSAdrian Chadd return (-1); 439415d7ccaSAdrian Chadd if (f >= vm_ndomains || t >= vm_ndomains) 440415d7ccaSAdrian Chadd return (-1); 441415d7ccaSAdrian Chadd return (mem_locality[f * vm_ndomains + t]); 4426520495aSAdrian Chadd #else 4436520495aSAdrian Chadd return (-1); 4446520495aSAdrian Chadd #endif 445415d7ccaSAdrian Chadd } 446415d7ccaSAdrian Chadd 447*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 448415d7ccaSAdrian Chadd /* 449415d7ccaSAdrian Chadd * Outputs the VM locality table. 450415d7ccaSAdrian Chadd */ 451415d7ccaSAdrian Chadd static int 452415d7ccaSAdrian Chadd sysctl_vm_phys_locality(SYSCTL_HANDLER_ARGS) 453415d7ccaSAdrian Chadd { 454415d7ccaSAdrian Chadd struct sbuf sbuf; 455415d7ccaSAdrian Chadd int error, i, j; 456415d7ccaSAdrian Chadd 457415d7ccaSAdrian Chadd error = sysctl_wire_old_buffer(req, 0); 458415d7ccaSAdrian Chadd if (error != 0) 459415d7ccaSAdrian Chadd return (error); 460415d7ccaSAdrian Chadd sbuf_new_for_sysctl(&sbuf, NULL, 128, req); 461415d7ccaSAdrian Chadd 462415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "\n"); 463415d7ccaSAdrian Chadd 464415d7ccaSAdrian Chadd for (i = 0; i < vm_ndomains; i++) { 465415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "%d: ", i); 466415d7ccaSAdrian Chadd for (j = 0; j < vm_ndomains; j++) { 467415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "%d ", vm_phys_mem_affinity(i, j)); 468415d7ccaSAdrian Chadd } 469415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "\n"); 470415d7ccaSAdrian Chadd } 471415d7ccaSAdrian Chadd error = sbuf_finish(&sbuf); 472415d7ccaSAdrian Chadd sbuf_delete(&sbuf); 473415d7ccaSAdrian Chadd return (error); 474415d7ccaSAdrian Chadd } 4756520495aSAdrian Chadd #endif 476415d7ccaSAdrian Chadd 4777e226537SAttilio Rao static void 4787e226537SAttilio Rao vm_freelist_add(struct vm_freelist *fl, vm_page_t m, int order, int tail) 479a3870a18SJohn Baldwin { 480a3870a18SJohn Baldwin 4817e226537SAttilio Rao m->order = order; 4827e226537SAttilio Rao if (tail) 483c325e866SKonstantin Belousov TAILQ_INSERT_TAIL(&fl[order].pl, m, plinks.q); 4847e226537SAttilio Rao else 485c325e866SKonstantin Belousov TAILQ_INSERT_HEAD(&fl[order].pl, m, plinks.q); 4867e226537SAttilio Rao fl[order].lcnt++; 487a3870a18SJohn Baldwin } 4887e226537SAttilio Rao 4897e226537SAttilio Rao static void 4907e226537SAttilio Rao vm_freelist_rem(struct vm_freelist *fl, vm_page_t m, int order) 4917e226537SAttilio Rao { 4927e226537SAttilio Rao 493c325e866SKonstantin Belousov TAILQ_REMOVE(&fl[order].pl, m, plinks.q); 4947e226537SAttilio Rao fl[order].lcnt--; 4957e226537SAttilio Rao m->order = VM_NFREEORDER; 496a3870a18SJohn Baldwin } 497a3870a18SJohn Baldwin 49811752d88SAlan Cox /* 49911752d88SAlan Cox * Create a physical memory segment. 50011752d88SAlan Cox */ 50111752d88SAlan Cox static void 502d866a563SAlan Cox _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int domain) 50311752d88SAlan Cox { 50411752d88SAlan Cox struct vm_phys_seg *seg; 50511752d88SAlan Cox 50611752d88SAlan Cox KASSERT(vm_phys_nsegs < VM_PHYSSEG_MAX, 50711752d88SAlan Cox ("vm_phys_create_seg: increase VM_PHYSSEG_MAX")); 5087e226537SAttilio Rao KASSERT(domain < vm_ndomains, 5097e226537SAttilio Rao ("vm_phys_create_seg: invalid domain provided")); 51011752d88SAlan Cox seg = &vm_phys_segs[vm_phys_nsegs++]; 511271f0f12SAlan Cox while (seg > vm_phys_segs && (seg - 1)->start >= end) { 512271f0f12SAlan Cox *seg = *(seg - 1); 513271f0f12SAlan Cox seg--; 514271f0f12SAlan Cox } 51511752d88SAlan Cox seg->start = start; 51611752d88SAlan Cox seg->end = end; 517a3870a18SJohn Baldwin seg->domain = domain; 51811752d88SAlan Cox } 51911752d88SAlan Cox 520a3870a18SJohn Baldwin static void 521d866a563SAlan Cox vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end) 522a3870a18SJohn Baldwin { 523*62d70a81SJohn Baldwin #ifdef VM_NUMA_ALLOC 524a3870a18SJohn Baldwin int i; 525a3870a18SJohn Baldwin 526a3870a18SJohn Baldwin if (mem_affinity == NULL) { 527d866a563SAlan Cox _vm_phys_create_seg(start, end, 0); 528a3870a18SJohn Baldwin return; 529a3870a18SJohn Baldwin } 530a3870a18SJohn Baldwin 531a3870a18SJohn Baldwin for (i = 0;; i++) { 532a3870a18SJohn Baldwin if (mem_affinity[i].end == 0) 533a3870a18SJohn Baldwin panic("Reached end of affinity info"); 534a3870a18SJohn Baldwin if (mem_affinity[i].end <= start) 535a3870a18SJohn Baldwin continue; 536a3870a18SJohn Baldwin if (mem_affinity[i].start > start) 537a3870a18SJohn Baldwin panic("No affinity info for start %jx", 538a3870a18SJohn Baldwin (uintmax_t)start); 539a3870a18SJohn Baldwin if (mem_affinity[i].end >= end) { 540d866a563SAlan Cox _vm_phys_create_seg(start, end, 541a3870a18SJohn Baldwin mem_affinity[i].domain); 542a3870a18SJohn Baldwin break; 543a3870a18SJohn Baldwin } 544d866a563SAlan Cox _vm_phys_create_seg(start, mem_affinity[i].end, 545a3870a18SJohn Baldwin mem_affinity[i].domain); 546a3870a18SJohn Baldwin start = mem_affinity[i].end; 547a3870a18SJohn Baldwin } 548*62d70a81SJohn Baldwin #else 549*62d70a81SJohn Baldwin _vm_phys_create_seg(start, end, 0); 550*62d70a81SJohn Baldwin #endif 551a3870a18SJohn Baldwin } 552a3870a18SJohn Baldwin 55311752d88SAlan Cox /* 554271f0f12SAlan Cox * Add a physical memory segment. 555271f0f12SAlan Cox */ 556271f0f12SAlan Cox void 557271f0f12SAlan Cox vm_phys_add_seg(vm_paddr_t start, vm_paddr_t end) 558271f0f12SAlan Cox { 559d866a563SAlan Cox vm_paddr_t paddr; 560271f0f12SAlan Cox 561271f0f12SAlan Cox KASSERT((start & PAGE_MASK) == 0, 562271f0f12SAlan Cox ("vm_phys_define_seg: start is not page aligned")); 563271f0f12SAlan Cox KASSERT((end & PAGE_MASK) == 0, 564271f0f12SAlan Cox ("vm_phys_define_seg: end is not page aligned")); 565d866a563SAlan Cox 566d866a563SAlan Cox /* 567d866a563SAlan Cox * Split the physical memory segment if it spans two or more free 568d866a563SAlan Cox * list boundaries. 569d866a563SAlan Cox */ 570d866a563SAlan Cox paddr = start; 571271f0f12SAlan Cox #ifdef VM_FREELIST_ISADMA 572d866a563SAlan Cox if (paddr < VM_ISADMA_BOUNDARY && end > VM_ISADMA_BOUNDARY) { 573d866a563SAlan Cox vm_phys_create_seg(paddr, VM_ISADMA_BOUNDARY); 574d866a563SAlan Cox paddr = VM_ISADMA_BOUNDARY; 575d866a563SAlan Cox } 576271f0f12SAlan Cox #endif 577d866a563SAlan Cox #ifdef VM_FREELIST_LOWMEM 578d866a563SAlan Cox if (paddr < VM_LOWMEM_BOUNDARY && end > VM_LOWMEM_BOUNDARY) { 579d866a563SAlan Cox vm_phys_create_seg(paddr, VM_LOWMEM_BOUNDARY); 580d866a563SAlan Cox paddr = VM_LOWMEM_BOUNDARY; 581d866a563SAlan Cox } 582271f0f12SAlan Cox #endif 583d866a563SAlan Cox #ifdef VM_FREELIST_DMA32 584d866a563SAlan Cox if (paddr < VM_DMA32_BOUNDARY && end > VM_DMA32_BOUNDARY) { 585d866a563SAlan Cox vm_phys_create_seg(paddr, VM_DMA32_BOUNDARY); 586d866a563SAlan Cox paddr = VM_DMA32_BOUNDARY; 587d866a563SAlan Cox } 588d866a563SAlan Cox #endif 589d866a563SAlan Cox vm_phys_create_seg(paddr, end); 590271f0f12SAlan Cox } 591271f0f12SAlan Cox 592271f0f12SAlan Cox /* 59311752d88SAlan Cox * Initialize the physical memory allocator. 594d866a563SAlan Cox * 595d866a563SAlan Cox * Requires that vm_page_array is initialized! 59611752d88SAlan Cox */ 59711752d88SAlan Cox void 59811752d88SAlan Cox vm_phys_init(void) 59911752d88SAlan Cox { 60011752d88SAlan Cox struct vm_freelist *fl; 601271f0f12SAlan Cox struct vm_phys_seg *seg; 602d866a563SAlan Cox u_long npages; 603d866a563SAlan Cox int dom, flind, freelist, oind, pind, segind; 60411752d88SAlan Cox 605d866a563SAlan Cox /* 606d866a563SAlan Cox * Compute the number of free lists, and generate the mapping from the 607d866a563SAlan Cox * manifest constants VM_FREELIST_* to the free list indices. 608d866a563SAlan Cox * 609d866a563SAlan Cox * Initially, the entries of vm_freelist_to_flind[] are set to either 610d866a563SAlan Cox * 0 or 1 to indicate which free lists should be created. 611d866a563SAlan Cox */ 612d866a563SAlan Cox npages = 0; 613d866a563SAlan Cox for (segind = vm_phys_nsegs - 1; segind >= 0; segind--) { 614d866a563SAlan Cox seg = &vm_phys_segs[segind]; 615d866a563SAlan Cox #ifdef VM_FREELIST_ISADMA 616d866a563SAlan Cox if (seg->end <= VM_ISADMA_BOUNDARY) 617d866a563SAlan Cox vm_freelist_to_flind[VM_FREELIST_ISADMA] = 1; 618d866a563SAlan Cox else 619d866a563SAlan Cox #endif 620d866a563SAlan Cox #ifdef VM_FREELIST_LOWMEM 621d866a563SAlan Cox if (seg->end <= VM_LOWMEM_BOUNDARY) 622d866a563SAlan Cox vm_freelist_to_flind[VM_FREELIST_LOWMEM] = 1; 623d866a563SAlan Cox else 624d866a563SAlan Cox #endif 625d866a563SAlan Cox #ifdef VM_FREELIST_DMA32 626d866a563SAlan Cox if ( 627d866a563SAlan Cox #ifdef VM_DMA32_NPAGES_THRESHOLD 628d866a563SAlan Cox /* 629d866a563SAlan Cox * Create the DMA32 free list only if the amount of 630d866a563SAlan Cox * physical memory above physical address 4G exceeds the 631d866a563SAlan Cox * given threshold. 632d866a563SAlan Cox */ 633d866a563SAlan Cox npages > VM_DMA32_NPAGES_THRESHOLD && 634d866a563SAlan Cox #endif 635d866a563SAlan Cox seg->end <= VM_DMA32_BOUNDARY) 636d866a563SAlan Cox vm_freelist_to_flind[VM_FREELIST_DMA32] = 1; 637d866a563SAlan Cox else 638d866a563SAlan Cox #endif 639d866a563SAlan Cox { 640d866a563SAlan Cox npages += atop(seg->end - seg->start); 641d866a563SAlan Cox vm_freelist_to_flind[VM_FREELIST_DEFAULT] = 1; 642d866a563SAlan Cox } 643d866a563SAlan Cox } 644d866a563SAlan Cox /* Change each entry into a running total of the free lists. */ 645d866a563SAlan Cox for (freelist = 1; freelist < VM_NFREELIST; freelist++) { 646d866a563SAlan Cox vm_freelist_to_flind[freelist] += 647d866a563SAlan Cox vm_freelist_to_flind[freelist - 1]; 648d866a563SAlan Cox } 649d866a563SAlan Cox vm_nfreelists = vm_freelist_to_flind[VM_NFREELIST - 1]; 650d866a563SAlan Cox KASSERT(vm_nfreelists > 0, ("vm_phys_init: no free lists")); 651d866a563SAlan Cox /* Change each entry into a free list index. */ 652d866a563SAlan Cox for (freelist = 0; freelist < VM_NFREELIST; freelist++) 653d866a563SAlan Cox vm_freelist_to_flind[freelist]--; 654d866a563SAlan Cox 655d866a563SAlan Cox /* 656d866a563SAlan Cox * Initialize the first_page and free_queues fields of each physical 657d866a563SAlan Cox * memory segment. 658d866a563SAlan Cox */ 659271f0f12SAlan Cox #ifdef VM_PHYSSEG_SPARSE 660d866a563SAlan Cox npages = 0; 66111752d88SAlan Cox #endif 662271f0f12SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 663271f0f12SAlan Cox seg = &vm_phys_segs[segind]; 664271f0f12SAlan Cox #ifdef VM_PHYSSEG_SPARSE 665d866a563SAlan Cox seg->first_page = &vm_page_array[npages]; 666d866a563SAlan Cox npages += atop(seg->end - seg->start); 667271f0f12SAlan Cox #else 668271f0f12SAlan Cox seg->first_page = PHYS_TO_VM_PAGE(seg->start); 66911752d88SAlan Cox #endif 670d866a563SAlan Cox #ifdef VM_FREELIST_ISADMA 671d866a563SAlan Cox if (seg->end <= VM_ISADMA_BOUNDARY) { 672d866a563SAlan Cox flind = vm_freelist_to_flind[VM_FREELIST_ISADMA]; 673d866a563SAlan Cox KASSERT(flind >= 0, 674d866a563SAlan Cox ("vm_phys_init: ISADMA flind < 0")); 675d866a563SAlan Cox } else 676d866a563SAlan Cox #endif 677d866a563SAlan Cox #ifdef VM_FREELIST_LOWMEM 678d866a563SAlan Cox if (seg->end <= VM_LOWMEM_BOUNDARY) { 679d866a563SAlan Cox flind = vm_freelist_to_flind[VM_FREELIST_LOWMEM]; 680d866a563SAlan Cox KASSERT(flind >= 0, 681d866a563SAlan Cox ("vm_phys_init: LOWMEM flind < 0")); 682d866a563SAlan Cox } else 683d866a563SAlan Cox #endif 684d866a563SAlan Cox #ifdef VM_FREELIST_DMA32 685d866a563SAlan Cox if (seg->end <= VM_DMA32_BOUNDARY) { 686d866a563SAlan Cox flind = vm_freelist_to_flind[VM_FREELIST_DMA32]; 687d866a563SAlan Cox KASSERT(flind >= 0, 688d866a563SAlan Cox ("vm_phys_init: DMA32 flind < 0")); 689d866a563SAlan Cox } else 690d866a563SAlan Cox #endif 691d866a563SAlan Cox { 692d866a563SAlan Cox flind = vm_freelist_to_flind[VM_FREELIST_DEFAULT]; 693d866a563SAlan Cox KASSERT(flind >= 0, 694d866a563SAlan Cox ("vm_phys_init: DEFAULT flind < 0")); 69511752d88SAlan Cox } 696d866a563SAlan Cox seg->free_queues = &vm_phys_free_queues[seg->domain][flind]; 697d866a563SAlan Cox } 698d866a563SAlan Cox 699d866a563SAlan Cox /* 700d866a563SAlan Cox * Initialize the free queues. 701d866a563SAlan Cox */ 7027e226537SAttilio Rao for (dom = 0; dom < vm_ndomains; dom++) { 70311752d88SAlan Cox for (flind = 0; flind < vm_nfreelists; flind++) { 70411752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 7057e226537SAttilio Rao fl = vm_phys_free_queues[dom][flind][pind]; 70611752d88SAlan Cox for (oind = 0; oind < VM_NFREEORDER; oind++) 70711752d88SAlan Cox TAILQ_INIT(&fl[oind].pl); 70811752d88SAlan Cox } 70911752d88SAlan Cox } 710a3870a18SJohn Baldwin } 711d866a563SAlan Cox 71238d6b2dcSRoger Pau Monné rw_init(&vm_phys_fictitious_reg_lock, "vmfctr"); 71311752d88SAlan Cox } 71411752d88SAlan Cox 71511752d88SAlan Cox /* 71611752d88SAlan Cox * Split a contiguous, power of two-sized set of physical pages. 71711752d88SAlan Cox */ 71811752d88SAlan Cox static __inline void 71911752d88SAlan Cox vm_phys_split_pages(vm_page_t m, int oind, struct vm_freelist *fl, int order) 72011752d88SAlan Cox { 72111752d88SAlan Cox vm_page_t m_buddy; 72211752d88SAlan Cox 72311752d88SAlan Cox while (oind > order) { 72411752d88SAlan Cox oind--; 72511752d88SAlan Cox m_buddy = &m[1 << oind]; 72611752d88SAlan Cox KASSERT(m_buddy->order == VM_NFREEORDER, 72711752d88SAlan Cox ("vm_phys_split_pages: page %p has unexpected order %d", 72811752d88SAlan Cox m_buddy, m_buddy->order)); 7297e226537SAttilio Rao vm_freelist_add(fl, m_buddy, oind, 0); 73011752d88SAlan Cox } 73111752d88SAlan Cox } 73211752d88SAlan Cox 73311752d88SAlan Cox /* 73411752d88SAlan Cox * Initialize a physical page and add it to the free lists. 73511752d88SAlan Cox */ 73611752d88SAlan Cox void 73711752d88SAlan Cox vm_phys_add_page(vm_paddr_t pa) 73811752d88SAlan Cox { 73911752d88SAlan Cox vm_page_t m; 740449c2e92SKonstantin Belousov struct vm_domain *vmd; 74111752d88SAlan Cox 74244f1c916SBryan Drewery vm_cnt.v_page_count++; 74311752d88SAlan Cox m = vm_phys_paddr_to_vm_page(pa); 74411752d88SAlan Cox m->phys_addr = pa; 74544e46b9eSAlan Cox m->queue = PQ_NONE; 74611752d88SAlan Cox m->segind = vm_phys_paddr_to_segind(pa); 747449c2e92SKonstantin Belousov vmd = vm_phys_domain(m); 748449c2e92SKonstantin Belousov vmd->vmd_page_count++; 749449c2e92SKonstantin Belousov vmd->vmd_segs |= 1UL << m->segind; 75011752d88SAlan Cox KASSERT(m->order == VM_NFREEORDER, 75111752d88SAlan Cox ("vm_phys_add_page: page %p has unexpected order %d", 75211752d88SAlan Cox m, m->order)); 75311752d88SAlan Cox m->pool = VM_FREEPOOL_DEFAULT; 75411752d88SAlan Cox pmap_page_init(m); 7558941dc44SAlan Cox mtx_lock(&vm_page_queue_free_mtx); 756449c2e92SKonstantin Belousov vm_phys_freecnt_adj(m, 1); 75711752d88SAlan Cox vm_phys_free_pages(m, 0); 7588941dc44SAlan Cox mtx_unlock(&vm_page_queue_free_mtx); 75911752d88SAlan Cox } 76011752d88SAlan Cox 76111752d88SAlan Cox /* 76211752d88SAlan Cox * Allocate a contiguous, power of two-sized set of physical pages 76311752d88SAlan Cox * from the free lists. 7648941dc44SAlan Cox * 7658941dc44SAlan Cox * The free page queues must be locked. 76611752d88SAlan Cox */ 76711752d88SAlan Cox vm_page_t 76811752d88SAlan Cox vm_phys_alloc_pages(int pool, int order) 76911752d88SAlan Cox { 77049ca10d4SJayachandran C. vm_page_t m; 7716520495aSAdrian Chadd int domain, flind; 7726520495aSAdrian Chadd struct vm_domain_iterator vi; 77349ca10d4SJayachandran C. 774f5c4b077SJohn Baldwin KASSERT(pool < VM_NFREEPOOL, 775f5c4b077SJohn Baldwin ("vm_phys_alloc_pages: pool %d is out of range", pool)); 776f5c4b077SJohn Baldwin KASSERT(order < VM_NFREEORDER, 777f5c4b077SJohn Baldwin ("vm_phys_alloc_pages: order %d is out of range", order)); 778f5c4b077SJohn Baldwin 7796520495aSAdrian Chadd vm_policy_iterator_init(&vi); 7806520495aSAdrian Chadd 7816520495aSAdrian Chadd while ((vm_domain_iterator_run(&vi, &domain)) == 0) { 78249ca10d4SJayachandran C. for (flind = 0; flind < vm_nfreelists; flind++) { 7837e226537SAttilio Rao m = vm_phys_alloc_domain_pages(domain, flind, pool, 7847e226537SAttilio Rao order); 78549ca10d4SJayachandran C. if (m != NULL) 78649ca10d4SJayachandran C. return (m); 78749ca10d4SJayachandran C. } 7887e226537SAttilio Rao } 7896520495aSAdrian Chadd 7906520495aSAdrian Chadd vm_policy_iterator_finish(&vi); 79149ca10d4SJayachandran C. return (NULL); 79249ca10d4SJayachandran C. } 79349ca10d4SJayachandran C. 79449ca10d4SJayachandran C. /* 795d866a563SAlan Cox * Allocate a contiguous, power of two-sized set of physical pages from the 796d866a563SAlan Cox * specified free list. The free list must be specified using one of the 797d866a563SAlan Cox * manifest constants VM_FREELIST_*. 798d866a563SAlan Cox * 799d866a563SAlan Cox * The free page queues must be locked. 80049ca10d4SJayachandran C. */ 80149ca10d4SJayachandran C. vm_page_t 802d866a563SAlan Cox vm_phys_alloc_freelist_pages(int freelist, int pool, int order) 80349ca10d4SJayachandran C. { 80411752d88SAlan Cox vm_page_t m; 8056520495aSAdrian Chadd struct vm_domain_iterator vi; 8066520495aSAdrian Chadd int domain; 80711752d88SAlan Cox 808d866a563SAlan Cox KASSERT(freelist < VM_NFREELIST, 809d866a563SAlan Cox ("vm_phys_alloc_freelist_pages: freelist %d is out of range", 810d866a563SAlan Cox freelist)); 81111752d88SAlan Cox KASSERT(pool < VM_NFREEPOOL, 81249ca10d4SJayachandran C. ("vm_phys_alloc_freelist_pages: pool %d is out of range", pool)); 81311752d88SAlan Cox KASSERT(order < VM_NFREEORDER, 81449ca10d4SJayachandran C. ("vm_phys_alloc_freelist_pages: order %d is out of range", order)); 8156520495aSAdrian Chadd 8166520495aSAdrian Chadd vm_policy_iterator_init(&vi); 8176520495aSAdrian Chadd 8186520495aSAdrian Chadd while ((vm_domain_iterator_run(&vi, &domain)) == 0) { 819d866a563SAlan Cox m = vm_phys_alloc_domain_pages(domain, 820d866a563SAlan Cox vm_freelist_to_flind[freelist], pool, order); 821f5c4b077SJohn Baldwin if (m != NULL) 822f5c4b077SJohn Baldwin return (m); 8237e226537SAttilio Rao } 8246520495aSAdrian Chadd 8256520495aSAdrian Chadd vm_policy_iterator_finish(&vi); 8267e226537SAttilio Rao return (NULL); 827f5c4b077SJohn Baldwin } 828f5c4b077SJohn Baldwin 829f5c4b077SJohn Baldwin static vm_page_t 830f5c4b077SJohn Baldwin vm_phys_alloc_domain_pages(int domain, int flind, int pool, int order) 831f5c4b077SJohn Baldwin { 832f5c4b077SJohn Baldwin struct vm_freelist *fl; 833f5c4b077SJohn Baldwin struct vm_freelist *alt; 834f5c4b077SJohn Baldwin int oind, pind; 835f5c4b077SJohn Baldwin vm_page_t m; 836f5c4b077SJohn Baldwin 83711752d88SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 8387e226537SAttilio Rao fl = &vm_phys_free_queues[domain][flind][pool][0]; 83911752d88SAlan Cox for (oind = order; oind < VM_NFREEORDER; oind++) { 84011752d88SAlan Cox m = TAILQ_FIRST(&fl[oind].pl); 84111752d88SAlan Cox if (m != NULL) { 8427e226537SAttilio Rao vm_freelist_rem(fl, m, oind); 84311752d88SAlan Cox vm_phys_split_pages(m, oind, fl, order); 84411752d88SAlan Cox return (m); 84511752d88SAlan Cox } 84611752d88SAlan Cox } 84711752d88SAlan Cox 84811752d88SAlan Cox /* 84911752d88SAlan Cox * The given pool was empty. Find the largest 85011752d88SAlan Cox * contiguous, power-of-two-sized set of pages in any 85111752d88SAlan Cox * pool. Transfer these pages to the given pool, and 85211752d88SAlan Cox * use them to satisfy the allocation. 85311752d88SAlan Cox */ 85411752d88SAlan Cox for (oind = VM_NFREEORDER - 1; oind >= order; oind--) { 85511752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 8567e226537SAttilio Rao alt = &vm_phys_free_queues[domain][flind][pind][0]; 85711752d88SAlan Cox m = TAILQ_FIRST(&alt[oind].pl); 85811752d88SAlan Cox if (m != NULL) { 8597e226537SAttilio Rao vm_freelist_rem(alt, m, oind); 86011752d88SAlan Cox vm_phys_set_pool(pool, m, oind); 86111752d88SAlan Cox vm_phys_split_pages(m, oind, fl, order); 86211752d88SAlan Cox return (m); 86311752d88SAlan Cox } 86411752d88SAlan Cox } 86511752d88SAlan Cox } 86611752d88SAlan Cox return (NULL); 86711752d88SAlan Cox } 86811752d88SAlan Cox 86911752d88SAlan Cox /* 87011752d88SAlan Cox * Find the vm_page corresponding to the given physical address. 87111752d88SAlan Cox */ 87211752d88SAlan Cox vm_page_t 87311752d88SAlan Cox vm_phys_paddr_to_vm_page(vm_paddr_t pa) 87411752d88SAlan Cox { 87511752d88SAlan Cox struct vm_phys_seg *seg; 87611752d88SAlan Cox int segind; 87711752d88SAlan Cox 87811752d88SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 87911752d88SAlan Cox seg = &vm_phys_segs[segind]; 88011752d88SAlan Cox if (pa >= seg->start && pa < seg->end) 88111752d88SAlan Cox return (&seg->first_page[atop(pa - seg->start)]); 88211752d88SAlan Cox } 883f06a3a36SAndrew Thompson return (NULL); 88411752d88SAlan Cox } 88511752d88SAlan Cox 886b6de32bdSKonstantin Belousov vm_page_t 887b6de32bdSKonstantin Belousov vm_phys_fictitious_to_vm_page(vm_paddr_t pa) 888b6de32bdSKonstantin Belousov { 88938d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg tmp, *seg; 890b6de32bdSKonstantin Belousov vm_page_t m; 891b6de32bdSKonstantin Belousov 892b6de32bdSKonstantin Belousov m = NULL; 89338d6b2dcSRoger Pau Monné tmp.start = pa; 89438d6b2dcSRoger Pau Monné tmp.end = 0; 89538d6b2dcSRoger Pau Monné 89638d6b2dcSRoger Pau Monné rw_rlock(&vm_phys_fictitious_reg_lock); 89738d6b2dcSRoger Pau Monné seg = RB_FIND(fict_tree, &vm_phys_fictitious_tree, &tmp); 89838d6b2dcSRoger Pau Monné rw_runlock(&vm_phys_fictitious_reg_lock); 89938d6b2dcSRoger Pau Monné if (seg == NULL) 90038d6b2dcSRoger Pau Monné return (NULL); 90138d6b2dcSRoger Pau Monné 902b6de32bdSKonstantin Belousov m = &seg->first_page[atop(pa - seg->start)]; 90338d6b2dcSRoger Pau Monné KASSERT((m->flags & PG_FICTITIOUS) != 0, ("%p not fictitious", m)); 90438d6b2dcSRoger Pau Monné 905b6de32bdSKonstantin Belousov return (m); 906b6de32bdSKonstantin Belousov } 907b6de32bdSKonstantin Belousov 9085ebe728dSRoger Pau Monné static inline void 9095ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(vm_page_t range, vm_paddr_t start, 9105ebe728dSRoger Pau Monné long page_count, vm_memattr_t memattr) 9115ebe728dSRoger Pau Monné { 9125ebe728dSRoger Pau Monné long i; 9135ebe728dSRoger Pau Monné 9145ebe728dSRoger Pau Monné for (i = 0; i < page_count; i++) { 9155ebe728dSRoger Pau Monné vm_page_initfake(&range[i], start + PAGE_SIZE * i, memattr); 9165ebe728dSRoger Pau Monné range[i].oflags &= ~VPO_UNMANAGED; 9175ebe728dSRoger Pau Monné range[i].busy_lock = VPB_UNBUSIED; 9185ebe728dSRoger Pau Monné } 9195ebe728dSRoger Pau Monné } 9205ebe728dSRoger Pau Monné 921b6de32bdSKonstantin Belousov int 922b6de32bdSKonstantin Belousov vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end, 923b6de32bdSKonstantin Belousov vm_memattr_t memattr) 924b6de32bdSKonstantin Belousov { 925b6de32bdSKonstantin Belousov struct vm_phys_fictitious_seg *seg; 926b6de32bdSKonstantin Belousov vm_page_t fp; 9275ebe728dSRoger Pau Monné long page_count; 928b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 9295ebe728dSRoger Pau Monné long pi, pe; 9305ebe728dSRoger Pau Monné long dpage_count; 931b6de32bdSKonstantin Belousov #endif 932b6de32bdSKonstantin Belousov 9335ebe728dSRoger Pau Monné KASSERT(start < end, 9345ebe728dSRoger Pau Monné ("Start of segment isn't less than end (start: %jx end: %jx)", 9355ebe728dSRoger Pau Monné (uintmax_t)start, (uintmax_t)end)); 9365ebe728dSRoger Pau Monné 937b6de32bdSKonstantin Belousov page_count = (end - start) / PAGE_SIZE; 938b6de32bdSKonstantin Belousov 939b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 940b6de32bdSKonstantin Belousov pi = atop(start); 9415ebe728dSRoger Pau Monné pe = atop(end); 9425ebe728dSRoger Pau Monné if (pi >= first_page && (pi - first_page) < vm_page_array_size) { 943b6de32bdSKonstantin Belousov fp = &vm_page_array[pi - first_page]; 9445ebe728dSRoger Pau Monné if ((pe - first_page) > vm_page_array_size) { 9455ebe728dSRoger Pau Monné /* 9465ebe728dSRoger Pau Monné * We have a segment that starts inside 9475ebe728dSRoger Pau Monné * of vm_page_array, but ends outside of it. 9485ebe728dSRoger Pau Monné * 9495ebe728dSRoger Pau Monné * Use vm_page_array pages for those that are 9505ebe728dSRoger Pau Monné * inside of the vm_page_array range, and 9515ebe728dSRoger Pau Monné * allocate the remaining ones. 9525ebe728dSRoger Pau Monné */ 9535ebe728dSRoger Pau Monné dpage_count = vm_page_array_size - (pi - first_page); 9545ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, start, dpage_count, 9555ebe728dSRoger Pau Monné memattr); 9565ebe728dSRoger Pau Monné page_count -= dpage_count; 9575ebe728dSRoger Pau Monné start += ptoa(dpage_count); 9585ebe728dSRoger Pau Monné goto alloc; 9595ebe728dSRoger Pau Monné } 9605ebe728dSRoger Pau Monné /* 9615ebe728dSRoger Pau Monné * We can allocate the full range from vm_page_array, 9625ebe728dSRoger Pau Monné * so there's no need to register the range in the tree. 9635ebe728dSRoger Pau Monné */ 9645ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, start, page_count, memattr); 9655ebe728dSRoger Pau Monné return (0); 9665ebe728dSRoger Pau Monné } else if (pe > first_page && (pe - first_page) < vm_page_array_size) { 9675ebe728dSRoger Pau Monné /* 9685ebe728dSRoger Pau Monné * We have a segment that ends inside of vm_page_array, 9695ebe728dSRoger Pau Monné * but starts outside of it. 9705ebe728dSRoger Pau Monné */ 9715ebe728dSRoger Pau Monné fp = &vm_page_array[0]; 9725ebe728dSRoger Pau Monné dpage_count = pe - first_page; 9735ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, ptoa(first_page), dpage_count, 9745ebe728dSRoger Pau Monné memattr); 9755ebe728dSRoger Pau Monné end -= ptoa(dpage_count); 9765ebe728dSRoger Pau Monné page_count -= dpage_count; 9775ebe728dSRoger Pau Monné goto alloc; 9785ebe728dSRoger Pau Monné } else if (pi < first_page && pe > (first_page + vm_page_array_size)) { 9795ebe728dSRoger Pau Monné /* 9805ebe728dSRoger Pau Monné * Trying to register a fictitious range that expands before 9815ebe728dSRoger Pau Monné * and after vm_page_array. 9825ebe728dSRoger Pau Monné */ 9835ebe728dSRoger Pau Monné return (EINVAL); 9845ebe728dSRoger Pau Monné } else { 9855ebe728dSRoger Pau Monné alloc: 986b6de32bdSKonstantin Belousov #endif 987b6de32bdSKonstantin Belousov fp = malloc(page_count * sizeof(struct vm_page), M_FICT_PAGES, 988b6de32bdSKonstantin Belousov M_WAITOK | M_ZERO); 9895ebe728dSRoger Pau Monné #ifdef VM_PHYSSEG_DENSE 990b6de32bdSKonstantin Belousov } 9915ebe728dSRoger Pau Monné #endif 9925ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, start, page_count, memattr); 99338d6b2dcSRoger Pau Monné 99438d6b2dcSRoger Pau Monné seg = malloc(sizeof(*seg), M_FICT_PAGES, M_WAITOK | M_ZERO); 995b6de32bdSKonstantin Belousov seg->start = start; 996b6de32bdSKonstantin Belousov seg->end = end; 997b6de32bdSKonstantin Belousov seg->first_page = fp; 99838d6b2dcSRoger Pau Monné 99938d6b2dcSRoger Pau Monné rw_wlock(&vm_phys_fictitious_reg_lock); 100038d6b2dcSRoger Pau Monné RB_INSERT(fict_tree, &vm_phys_fictitious_tree, seg); 100138d6b2dcSRoger Pau Monné rw_wunlock(&vm_phys_fictitious_reg_lock); 100238d6b2dcSRoger Pau Monné 1003b6de32bdSKonstantin Belousov return (0); 1004b6de32bdSKonstantin Belousov } 1005b6de32bdSKonstantin Belousov 1006b6de32bdSKonstantin Belousov void 1007b6de32bdSKonstantin Belousov vm_phys_fictitious_unreg_range(vm_paddr_t start, vm_paddr_t end) 1008b6de32bdSKonstantin Belousov { 100938d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *seg, tmp; 1010b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 10115ebe728dSRoger Pau Monné long pi, pe; 1012b6de32bdSKonstantin Belousov #endif 1013b6de32bdSKonstantin Belousov 10145ebe728dSRoger Pau Monné KASSERT(start < end, 10155ebe728dSRoger Pau Monné ("Start of segment isn't less than end (start: %jx end: %jx)", 10165ebe728dSRoger Pau Monné (uintmax_t)start, (uintmax_t)end)); 10175ebe728dSRoger Pau Monné 1018b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 1019b6de32bdSKonstantin Belousov pi = atop(start); 10205ebe728dSRoger Pau Monné pe = atop(end); 10215ebe728dSRoger Pau Monné if (pi >= first_page && (pi - first_page) < vm_page_array_size) { 10225ebe728dSRoger Pau Monné if ((pe - first_page) <= vm_page_array_size) { 10235ebe728dSRoger Pau Monné /* 10245ebe728dSRoger Pau Monné * This segment was allocated using vm_page_array 10255ebe728dSRoger Pau Monné * only, there's nothing to do since those pages 10265ebe728dSRoger Pau Monné * were never added to the tree. 10275ebe728dSRoger Pau Monné */ 10285ebe728dSRoger Pau Monné return; 10295ebe728dSRoger Pau Monné } 10305ebe728dSRoger Pau Monné /* 10315ebe728dSRoger Pau Monné * We have a segment that starts inside 10325ebe728dSRoger Pau Monné * of vm_page_array, but ends outside of it. 10335ebe728dSRoger Pau Monné * 10345ebe728dSRoger Pau Monné * Calculate how many pages were added to the 10355ebe728dSRoger Pau Monné * tree and free them. 10365ebe728dSRoger Pau Monné */ 10375ebe728dSRoger Pau Monné start = ptoa(first_page + vm_page_array_size); 10385ebe728dSRoger Pau Monné } else if (pe > first_page && (pe - first_page) < vm_page_array_size) { 10395ebe728dSRoger Pau Monné /* 10405ebe728dSRoger Pau Monné * We have a segment that ends inside of vm_page_array, 10415ebe728dSRoger Pau Monné * but starts outside of it. 10425ebe728dSRoger Pau Monné */ 10435ebe728dSRoger Pau Monné end = ptoa(first_page); 10445ebe728dSRoger Pau Monné } else if (pi < first_page && pe > (first_page + vm_page_array_size)) { 10455ebe728dSRoger Pau Monné /* Since it's not possible to register such a range, panic. */ 10465ebe728dSRoger Pau Monné panic( 10475ebe728dSRoger Pau Monné "Unregistering not registered fictitious range [%#jx:%#jx]", 10485ebe728dSRoger Pau Monné (uintmax_t)start, (uintmax_t)end); 10495ebe728dSRoger Pau Monné } 1050b6de32bdSKonstantin Belousov #endif 105138d6b2dcSRoger Pau Monné tmp.start = start; 105238d6b2dcSRoger Pau Monné tmp.end = 0; 1053b6de32bdSKonstantin Belousov 105438d6b2dcSRoger Pau Monné rw_wlock(&vm_phys_fictitious_reg_lock); 105538d6b2dcSRoger Pau Monné seg = RB_FIND(fict_tree, &vm_phys_fictitious_tree, &tmp); 105638d6b2dcSRoger Pau Monné if (seg->start != start || seg->end != end) { 105738d6b2dcSRoger Pau Monné rw_wunlock(&vm_phys_fictitious_reg_lock); 105838d6b2dcSRoger Pau Monné panic( 105938d6b2dcSRoger Pau Monné "Unregistering not registered fictitious range [%#jx:%#jx]", 106038d6b2dcSRoger Pau Monné (uintmax_t)start, (uintmax_t)end); 106138d6b2dcSRoger Pau Monné } 106238d6b2dcSRoger Pau Monné RB_REMOVE(fict_tree, &vm_phys_fictitious_tree, seg); 106338d6b2dcSRoger Pau Monné rw_wunlock(&vm_phys_fictitious_reg_lock); 106438d6b2dcSRoger Pau Monné free(seg->first_page, M_FICT_PAGES); 106538d6b2dcSRoger Pau Monné free(seg, M_FICT_PAGES); 1066b6de32bdSKonstantin Belousov } 1067b6de32bdSKonstantin Belousov 106811752d88SAlan Cox /* 106911752d88SAlan Cox * Find the segment containing the given physical address. 107011752d88SAlan Cox */ 107111752d88SAlan Cox static int 107211752d88SAlan Cox vm_phys_paddr_to_segind(vm_paddr_t pa) 107311752d88SAlan Cox { 107411752d88SAlan Cox struct vm_phys_seg *seg; 107511752d88SAlan Cox int segind; 107611752d88SAlan Cox 107711752d88SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 107811752d88SAlan Cox seg = &vm_phys_segs[segind]; 107911752d88SAlan Cox if (pa >= seg->start && pa < seg->end) 108011752d88SAlan Cox return (segind); 108111752d88SAlan Cox } 108211752d88SAlan Cox panic("vm_phys_paddr_to_segind: paddr %#jx is not in any segment" , 108311752d88SAlan Cox (uintmax_t)pa); 108411752d88SAlan Cox } 108511752d88SAlan Cox 108611752d88SAlan Cox /* 108711752d88SAlan Cox * Free a contiguous, power of two-sized set of physical pages. 10888941dc44SAlan Cox * 10898941dc44SAlan Cox * The free page queues must be locked. 109011752d88SAlan Cox */ 109111752d88SAlan Cox void 109211752d88SAlan Cox vm_phys_free_pages(vm_page_t m, int order) 109311752d88SAlan Cox { 109411752d88SAlan Cox struct vm_freelist *fl; 109511752d88SAlan Cox struct vm_phys_seg *seg; 10965c1f2cc4SAlan Cox vm_paddr_t pa; 109711752d88SAlan Cox vm_page_t m_buddy; 109811752d88SAlan Cox 109911752d88SAlan Cox KASSERT(m->order == VM_NFREEORDER, 11008941dc44SAlan Cox ("vm_phys_free_pages: page %p has unexpected order %d", 110111752d88SAlan Cox m, m->order)); 110211752d88SAlan Cox KASSERT(m->pool < VM_NFREEPOOL, 11038941dc44SAlan Cox ("vm_phys_free_pages: page %p has unexpected pool %d", 110411752d88SAlan Cox m, m->pool)); 110511752d88SAlan Cox KASSERT(order < VM_NFREEORDER, 11068941dc44SAlan Cox ("vm_phys_free_pages: order %d is out of range", order)); 110711752d88SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 110811752d88SAlan Cox seg = &vm_phys_segs[m->segind]; 11095c1f2cc4SAlan Cox if (order < VM_NFREEORDER - 1) { 11105c1f2cc4SAlan Cox pa = VM_PAGE_TO_PHYS(m); 11115c1f2cc4SAlan Cox do { 11125c1f2cc4SAlan Cox pa ^= ((vm_paddr_t)1 << (PAGE_SHIFT + order)); 11135c1f2cc4SAlan Cox if (pa < seg->start || pa >= seg->end) 111411752d88SAlan Cox break; 11155c1f2cc4SAlan Cox m_buddy = &seg->first_page[atop(pa - seg->start)]; 111611752d88SAlan Cox if (m_buddy->order != order) 111711752d88SAlan Cox break; 111811752d88SAlan Cox fl = (*seg->free_queues)[m_buddy->pool]; 11197e226537SAttilio Rao vm_freelist_rem(fl, m_buddy, order); 112011752d88SAlan Cox if (m_buddy->pool != m->pool) 112111752d88SAlan Cox vm_phys_set_pool(m->pool, m_buddy, order); 112211752d88SAlan Cox order++; 11235c1f2cc4SAlan Cox pa &= ~(((vm_paddr_t)1 << (PAGE_SHIFT + order)) - 1); 112411752d88SAlan Cox m = &seg->first_page[atop(pa - seg->start)]; 11255c1f2cc4SAlan Cox } while (order < VM_NFREEORDER - 1); 112611752d88SAlan Cox } 112711752d88SAlan Cox fl = (*seg->free_queues)[m->pool]; 11287e226537SAttilio Rao vm_freelist_add(fl, m, order, 1); 112911752d88SAlan Cox } 113011752d88SAlan Cox 113111752d88SAlan Cox /* 11325c1f2cc4SAlan Cox * Free a contiguous, arbitrarily sized set of physical pages. 11335c1f2cc4SAlan Cox * 11345c1f2cc4SAlan Cox * The free page queues must be locked. 11355c1f2cc4SAlan Cox */ 11365c1f2cc4SAlan Cox void 11375c1f2cc4SAlan Cox vm_phys_free_contig(vm_page_t m, u_long npages) 11385c1f2cc4SAlan Cox { 11395c1f2cc4SAlan Cox u_int n; 11405c1f2cc4SAlan Cox int order; 11415c1f2cc4SAlan Cox 11425c1f2cc4SAlan Cox /* 11435c1f2cc4SAlan Cox * Avoid unnecessary coalescing by freeing the pages in the largest 11445c1f2cc4SAlan Cox * possible power-of-two-sized subsets. 11455c1f2cc4SAlan Cox */ 11465c1f2cc4SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 11475c1f2cc4SAlan Cox for (;; npages -= n) { 11485c1f2cc4SAlan Cox /* 11495c1f2cc4SAlan Cox * Unsigned "min" is used here so that "order" is assigned 11505c1f2cc4SAlan Cox * "VM_NFREEORDER - 1" when "m"'s physical address is zero 11515c1f2cc4SAlan Cox * or the low-order bits of its physical address are zero 11525c1f2cc4SAlan Cox * because the size of a physical address exceeds the size of 11535c1f2cc4SAlan Cox * a long. 11545c1f2cc4SAlan Cox */ 11555c1f2cc4SAlan Cox order = min(ffsl(VM_PAGE_TO_PHYS(m) >> PAGE_SHIFT) - 1, 11565c1f2cc4SAlan Cox VM_NFREEORDER - 1); 11575c1f2cc4SAlan Cox n = 1 << order; 11585c1f2cc4SAlan Cox if (npages < n) 11595c1f2cc4SAlan Cox break; 11605c1f2cc4SAlan Cox vm_phys_free_pages(m, order); 11615c1f2cc4SAlan Cox m += n; 11625c1f2cc4SAlan Cox } 11635c1f2cc4SAlan Cox /* The residual "npages" is less than "1 << (VM_NFREEORDER - 1)". */ 11645c1f2cc4SAlan Cox for (; npages > 0; npages -= n) { 11655c1f2cc4SAlan Cox order = flsl(npages) - 1; 11665c1f2cc4SAlan Cox n = 1 << order; 11675c1f2cc4SAlan Cox vm_phys_free_pages(m, order); 11685c1f2cc4SAlan Cox m += n; 11695c1f2cc4SAlan Cox } 11705c1f2cc4SAlan Cox } 11715c1f2cc4SAlan Cox 11725c1f2cc4SAlan Cox /* 1173c869e672SAlan Cox * Scan physical memory between the specified addresses "low" and "high" for a 1174c869e672SAlan Cox * run of contiguous physical pages that satisfy the specified conditions, and 1175c869e672SAlan Cox * return the lowest page in the run. The specified "alignment" determines 1176c869e672SAlan Cox * the alignment of the lowest physical page in the run. If the specified 1177c869e672SAlan Cox * "boundary" is non-zero, then the run of physical pages cannot span a 1178c869e672SAlan Cox * physical address that is a multiple of "boundary". 1179c869e672SAlan Cox * 1180c869e672SAlan Cox * "npages" must be greater than zero. Both "alignment" and "boundary" must 1181c869e672SAlan Cox * be a power of two. 1182c869e672SAlan Cox */ 1183c869e672SAlan Cox vm_page_t 1184c869e672SAlan Cox vm_phys_scan_contig(u_long npages, vm_paddr_t low, vm_paddr_t high, 1185c869e672SAlan Cox u_long alignment, vm_paddr_t boundary, int options) 1186c869e672SAlan Cox { 1187c869e672SAlan Cox vm_paddr_t pa_end; 1188c869e672SAlan Cox vm_page_t m_end, m_run, m_start; 1189c869e672SAlan Cox struct vm_phys_seg *seg; 1190c869e672SAlan Cox int segind; 1191c869e672SAlan Cox 1192c869e672SAlan Cox KASSERT(npages > 0, ("npages is 0")); 1193c869e672SAlan Cox KASSERT(powerof2(alignment), ("alignment is not a power of 2")); 1194c869e672SAlan Cox KASSERT(powerof2(boundary), ("boundary is not a power of 2")); 1195c869e672SAlan Cox if (low >= high) 1196c869e672SAlan Cox return (NULL); 1197c869e672SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 1198c869e672SAlan Cox seg = &vm_phys_segs[segind]; 1199c869e672SAlan Cox if (seg->start >= high) 1200c869e672SAlan Cox break; 1201c869e672SAlan Cox if (low >= seg->end) 1202c869e672SAlan Cox continue; 1203c869e672SAlan Cox if (low <= seg->start) 1204c869e672SAlan Cox m_start = seg->first_page; 1205c869e672SAlan Cox else 1206c869e672SAlan Cox m_start = &seg->first_page[atop(low - seg->start)]; 1207c869e672SAlan Cox if (high < seg->end) 1208c869e672SAlan Cox pa_end = high; 1209c869e672SAlan Cox else 1210c869e672SAlan Cox pa_end = seg->end; 1211c869e672SAlan Cox if (pa_end - VM_PAGE_TO_PHYS(m_start) < ptoa(npages)) 1212c869e672SAlan Cox continue; 1213c869e672SAlan Cox m_end = &seg->first_page[atop(pa_end - seg->start)]; 1214c869e672SAlan Cox m_run = vm_page_scan_contig(npages, m_start, m_end, 1215c869e672SAlan Cox alignment, boundary, options); 1216c869e672SAlan Cox if (m_run != NULL) 1217c869e672SAlan Cox return (m_run); 1218c869e672SAlan Cox } 1219c869e672SAlan Cox return (NULL); 1220c869e672SAlan Cox } 1221c869e672SAlan Cox 1222c869e672SAlan Cox /* 122311752d88SAlan Cox * Set the pool for a contiguous, power of two-sized set of physical pages. 122411752d88SAlan Cox */ 12257bfda801SAlan Cox void 122611752d88SAlan Cox vm_phys_set_pool(int pool, vm_page_t m, int order) 122711752d88SAlan Cox { 122811752d88SAlan Cox vm_page_t m_tmp; 122911752d88SAlan Cox 123011752d88SAlan Cox for (m_tmp = m; m_tmp < &m[1 << order]; m_tmp++) 123111752d88SAlan Cox m_tmp->pool = pool; 123211752d88SAlan Cox } 123311752d88SAlan Cox 123411752d88SAlan Cox /* 12359742373aSAlan Cox * Search for the given physical page "m" in the free lists. If the search 12369742373aSAlan Cox * succeeds, remove "m" from the free lists and return TRUE. Otherwise, return 12379742373aSAlan Cox * FALSE, indicating that "m" is not in the free lists. 12387bfda801SAlan Cox * 12397bfda801SAlan Cox * The free page queues must be locked. 12407bfda801SAlan Cox */ 1241e35395ceSAlan Cox boolean_t 12427bfda801SAlan Cox vm_phys_unfree_page(vm_page_t m) 12437bfda801SAlan Cox { 12447bfda801SAlan Cox struct vm_freelist *fl; 12457bfda801SAlan Cox struct vm_phys_seg *seg; 12467bfda801SAlan Cox vm_paddr_t pa, pa_half; 12477bfda801SAlan Cox vm_page_t m_set, m_tmp; 12487bfda801SAlan Cox int order; 12497bfda801SAlan Cox 12507bfda801SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 12517bfda801SAlan Cox 12527bfda801SAlan Cox /* 12537bfda801SAlan Cox * First, find the contiguous, power of two-sized set of free 12547bfda801SAlan Cox * physical pages containing the given physical page "m" and 12557bfda801SAlan Cox * assign it to "m_set". 12567bfda801SAlan Cox */ 12577bfda801SAlan Cox seg = &vm_phys_segs[m->segind]; 12587bfda801SAlan Cox for (m_set = m, order = 0; m_set->order == VM_NFREEORDER && 1259bc8794a1SAlan Cox order < VM_NFREEORDER - 1; ) { 12607bfda801SAlan Cox order++; 12617bfda801SAlan Cox pa = m->phys_addr & (~(vm_paddr_t)0 << (PAGE_SHIFT + order)); 12622fbced65SAlan Cox if (pa >= seg->start) 12637bfda801SAlan Cox m_set = &seg->first_page[atop(pa - seg->start)]; 1264e35395ceSAlan Cox else 1265e35395ceSAlan Cox return (FALSE); 12667bfda801SAlan Cox } 1267e35395ceSAlan Cox if (m_set->order < order) 1268e35395ceSAlan Cox return (FALSE); 1269e35395ceSAlan Cox if (m_set->order == VM_NFREEORDER) 1270e35395ceSAlan Cox return (FALSE); 12717bfda801SAlan Cox KASSERT(m_set->order < VM_NFREEORDER, 12727bfda801SAlan Cox ("vm_phys_unfree_page: page %p has unexpected order %d", 12737bfda801SAlan Cox m_set, m_set->order)); 12747bfda801SAlan Cox 12757bfda801SAlan Cox /* 12767bfda801SAlan Cox * Next, remove "m_set" from the free lists. Finally, extract 12777bfda801SAlan Cox * "m" from "m_set" using an iterative algorithm: While "m_set" 12787bfda801SAlan Cox * is larger than a page, shrink "m_set" by returning the half 12797bfda801SAlan Cox * of "m_set" that does not contain "m" to the free lists. 12807bfda801SAlan Cox */ 12817bfda801SAlan Cox fl = (*seg->free_queues)[m_set->pool]; 12827bfda801SAlan Cox order = m_set->order; 12837e226537SAttilio Rao vm_freelist_rem(fl, m_set, order); 12847bfda801SAlan Cox while (order > 0) { 12857bfda801SAlan Cox order--; 12867bfda801SAlan Cox pa_half = m_set->phys_addr ^ (1 << (PAGE_SHIFT + order)); 12877bfda801SAlan Cox if (m->phys_addr < pa_half) 12887bfda801SAlan Cox m_tmp = &seg->first_page[atop(pa_half - seg->start)]; 12897bfda801SAlan Cox else { 12907bfda801SAlan Cox m_tmp = m_set; 12917bfda801SAlan Cox m_set = &seg->first_page[atop(pa_half - seg->start)]; 12927bfda801SAlan Cox } 12937e226537SAttilio Rao vm_freelist_add(fl, m_tmp, order, 0); 12947bfda801SAlan Cox } 12957bfda801SAlan Cox KASSERT(m_set == m, ("vm_phys_unfree_page: fatal inconsistency")); 1296e35395ceSAlan Cox return (TRUE); 12977bfda801SAlan Cox } 12987bfda801SAlan Cox 12997bfda801SAlan Cox /* 13007bfda801SAlan Cox * Try to zero one physical page. Used by an idle priority thread. 130111752d88SAlan Cox */ 130211752d88SAlan Cox boolean_t 130311752d88SAlan Cox vm_phys_zero_pages_idle(void) 130411752d88SAlan Cox { 13057e226537SAttilio Rao static struct vm_freelist *fl; 13067bfda801SAlan Cox static int flind, oind, pind; 130711752d88SAlan Cox vm_page_t m, m_tmp; 13087e226537SAttilio Rao int domain; 130911752d88SAlan Cox 13107e226537SAttilio Rao domain = vm_rr_selectdomain(); 13117e226537SAttilio Rao fl = vm_phys_free_queues[domain][0][0]; 131211752d88SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 13137bfda801SAlan Cox for (;;) { 1314c325e866SKonstantin Belousov TAILQ_FOREACH_REVERSE(m, &fl[oind].pl, pglist, plinks.q) { 13157bfda801SAlan Cox for (m_tmp = m; m_tmp < &m[1 << oind]; m_tmp++) { 13167bfda801SAlan Cox if ((m_tmp->flags & (PG_CACHED | PG_ZERO)) == 0) { 13177bfda801SAlan Cox vm_phys_unfree_page(m_tmp); 1318449c2e92SKonstantin Belousov vm_phys_freecnt_adj(m, -1); 131911752d88SAlan Cox mtx_unlock(&vm_page_queue_free_mtx); 132011752d88SAlan Cox pmap_zero_page_idle(m_tmp); 132111752d88SAlan Cox m_tmp->flags |= PG_ZERO; 132211752d88SAlan Cox mtx_lock(&vm_page_queue_free_mtx); 1323449c2e92SKonstantin Belousov vm_phys_freecnt_adj(m, 1); 13247bfda801SAlan Cox vm_phys_free_pages(m_tmp, 0); 13257bfda801SAlan Cox vm_page_zero_count++; 13267bfda801SAlan Cox cnt_prezero++; 132711752d88SAlan Cox return (TRUE); 132811752d88SAlan Cox } 132911752d88SAlan Cox } 133011752d88SAlan Cox } 13317bfda801SAlan Cox oind++; 13327bfda801SAlan Cox if (oind == VM_NFREEORDER) { 13337bfda801SAlan Cox oind = 0; 13347bfda801SAlan Cox pind++; 13357bfda801SAlan Cox if (pind == VM_NFREEPOOL) { 13367bfda801SAlan Cox pind = 0; 13377bfda801SAlan Cox flind++; 13387bfda801SAlan Cox if (flind == vm_nfreelists) 13397bfda801SAlan Cox flind = 0; 13407bfda801SAlan Cox } 13417e226537SAttilio Rao fl = vm_phys_free_queues[domain][flind][pind]; 13427bfda801SAlan Cox } 13437bfda801SAlan Cox } 134411752d88SAlan Cox } 134511752d88SAlan Cox 134611752d88SAlan Cox /* 13472f9f48d6SAlan Cox * Allocate a contiguous set of physical pages of the given size 13482f9f48d6SAlan Cox * "npages" from the free lists. All of the physical pages must be at 13492f9f48d6SAlan Cox * or above the given physical address "low" and below the given 13502f9f48d6SAlan Cox * physical address "high". The given value "alignment" determines the 13512f9f48d6SAlan Cox * alignment of the first physical page in the set. If the given value 13522f9f48d6SAlan Cox * "boundary" is non-zero, then the set of physical pages cannot cross 13532f9f48d6SAlan Cox * any physical address boundary that is a multiple of that value. Both 135411752d88SAlan Cox * "alignment" and "boundary" must be a power of two. 135511752d88SAlan Cox */ 135611752d88SAlan Cox vm_page_t 13575c1f2cc4SAlan Cox vm_phys_alloc_contig(u_long npages, vm_paddr_t low, vm_paddr_t high, 13585c1f2cc4SAlan Cox u_long alignment, vm_paddr_t boundary) 135911752d88SAlan Cox { 1360c869e672SAlan Cox vm_paddr_t pa_end, pa_start; 1361c869e672SAlan Cox vm_page_t m_run; 13626520495aSAdrian Chadd struct vm_domain_iterator vi; 1363c869e672SAlan Cox struct vm_phys_seg *seg; 1364c869e672SAlan Cox int domain, segind; 136511752d88SAlan Cox 1366c869e672SAlan Cox KASSERT(npages > 0, ("npages is 0")); 1367c869e672SAlan Cox KASSERT(powerof2(alignment), ("alignment is not a power of 2")); 1368c869e672SAlan Cox KASSERT(powerof2(boundary), ("boundary is not a power of 2")); 1369fbd80bd0SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 1370c869e672SAlan Cox if (low >= high) 1371c869e672SAlan Cox return (NULL); 13726520495aSAdrian Chadd vm_policy_iterator_init(&vi); 13737e226537SAttilio Rao restartdom: 13746520495aSAdrian Chadd if (vm_domain_iterator_run(&vi, &domain) != 0) { 13756520495aSAdrian Chadd vm_policy_iterator_finish(&vi); 13766520495aSAdrian Chadd return (NULL); 13776520495aSAdrian Chadd } 1378c869e672SAlan Cox m_run = NULL; 1379477bffbeSAlan Cox for (segind = vm_phys_nsegs - 1; segind >= 0; segind--) { 1380c869e672SAlan Cox seg = &vm_phys_segs[segind]; 1381477bffbeSAlan Cox if (seg->start >= high || seg->domain != domain) 138211752d88SAlan Cox continue; 1383477bffbeSAlan Cox if (low >= seg->end) 1384477bffbeSAlan Cox break; 1385c869e672SAlan Cox if (low <= seg->start) 1386c869e672SAlan Cox pa_start = seg->start; 1387c869e672SAlan Cox else 1388c869e672SAlan Cox pa_start = low; 1389c869e672SAlan Cox if (high < seg->end) 1390c869e672SAlan Cox pa_end = high; 1391c869e672SAlan Cox else 1392c869e672SAlan Cox pa_end = seg->end; 1393c869e672SAlan Cox if (pa_end - pa_start < ptoa(npages)) 1394c869e672SAlan Cox continue; 1395c869e672SAlan Cox m_run = vm_phys_alloc_seg_contig(seg, npages, low, high, 1396c869e672SAlan Cox alignment, boundary); 1397c869e672SAlan Cox if (m_run != NULL) 1398c869e672SAlan Cox break; 1399c869e672SAlan Cox } 1400c869e672SAlan Cox if (m_run == NULL && !vm_domain_iterator_isdone(&vi)) 1401c869e672SAlan Cox goto restartdom; 1402c869e672SAlan Cox vm_policy_iterator_finish(&vi); 1403c869e672SAlan Cox return (m_run); 1404c869e672SAlan Cox } 140511752d88SAlan Cox 140611752d88SAlan Cox /* 1407c869e672SAlan Cox * Allocate a run of contiguous physical pages from the free list for the 1408c869e672SAlan Cox * specified segment. 1409c869e672SAlan Cox */ 1410c869e672SAlan Cox static vm_page_t 1411c869e672SAlan Cox vm_phys_alloc_seg_contig(struct vm_phys_seg *seg, u_long npages, 1412c869e672SAlan Cox vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary) 1413c869e672SAlan Cox { 1414c869e672SAlan Cox struct vm_freelist *fl; 1415c869e672SAlan Cox vm_paddr_t pa, pa_end, size; 1416c869e672SAlan Cox vm_page_t m, m_ret; 1417c869e672SAlan Cox u_long npages_end; 1418c869e672SAlan Cox int oind, order, pind; 1419c869e672SAlan Cox 1420c869e672SAlan Cox KASSERT(npages > 0, ("npages is 0")); 1421c869e672SAlan Cox KASSERT(powerof2(alignment), ("alignment is not a power of 2")); 1422c869e672SAlan Cox KASSERT(powerof2(boundary), ("boundary is not a power of 2")); 1423c869e672SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 1424c869e672SAlan Cox /* Compute the queue that is the best fit for npages. */ 1425c869e672SAlan Cox for (order = 0; (1 << order) < npages; order++); 1426c869e672SAlan Cox /* Search for a run satisfying the specified conditions. */ 1427c869e672SAlan Cox size = npages << PAGE_SHIFT; 1428c869e672SAlan Cox for (oind = min(order, VM_NFREEORDER - 1); oind < VM_NFREEORDER; 1429c869e672SAlan Cox oind++) { 1430c869e672SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 1431c869e672SAlan Cox fl = (*seg->free_queues)[pind]; 1432c869e672SAlan Cox TAILQ_FOREACH(m_ret, &fl[oind].pl, plinks.q) { 1433c869e672SAlan Cox /* 143411752d88SAlan Cox * Is the size of this allocation request 143511752d88SAlan Cox * larger than the largest block size? 143611752d88SAlan Cox */ 143711752d88SAlan Cox if (order >= VM_NFREEORDER) { 143811752d88SAlan Cox /* 1439c869e672SAlan Cox * Determine if a sufficient number of 1440c869e672SAlan Cox * subsequent blocks to satisfy the 1441c869e672SAlan Cox * allocation request are free. 144211752d88SAlan Cox */ 144311752d88SAlan Cox pa = VM_PAGE_TO_PHYS(m_ret); 1444c869e672SAlan Cox pa_end = pa + size; 144511752d88SAlan Cox for (;;) { 1446c869e672SAlan Cox pa += 1 << (PAGE_SHIFT + 1447c869e672SAlan Cox VM_NFREEORDER - 1); 1448c869e672SAlan Cox if (pa >= pa_end || 1449c869e672SAlan Cox pa < seg->start || 145011752d88SAlan Cox pa >= seg->end) 145111752d88SAlan Cox break; 1452c869e672SAlan Cox m = &seg->first_page[atop(pa - 1453c869e672SAlan Cox seg->start)]; 1454c869e672SAlan Cox if (m->order != VM_NFREEORDER - 1455c869e672SAlan Cox 1) 145611752d88SAlan Cox break; 145711752d88SAlan Cox } 1458c869e672SAlan Cox /* If not, go to the next block. */ 1459c869e672SAlan Cox if (pa < pa_end) 146011752d88SAlan Cox continue; 146111752d88SAlan Cox } 146211752d88SAlan Cox 146311752d88SAlan Cox /* 1464c869e672SAlan Cox * Determine if the blocks are within the 1465c869e672SAlan Cox * given range, satisfy the given alignment, 1466c869e672SAlan Cox * and do not cross the given boundary. 146711752d88SAlan Cox */ 146811752d88SAlan Cox pa = VM_PAGE_TO_PHYS(m_ret); 1469c869e672SAlan Cox pa_end = pa + size; 1470c869e672SAlan Cox if (pa >= low && pa_end <= high && (pa & 1471c869e672SAlan Cox (alignment - 1)) == 0 && ((pa ^ (pa_end - 1472c869e672SAlan Cox 1)) & ~(boundary - 1)) == 0) 147311752d88SAlan Cox goto done; 147411752d88SAlan Cox } 147511752d88SAlan Cox } 147611752d88SAlan Cox } 147711752d88SAlan Cox return (NULL); 147811752d88SAlan Cox done: 147911752d88SAlan Cox for (m = m_ret; m < &m_ret[npages]; m = &m[1 << oind]) { 148011752d88SAlan Cox fl = (*seg->free_queues)[m->pool]; 14817e226537SAttilio Rao vm_freelist_rem(fl, m, m->order); 148211752d88SAlan Cox } 148311752d88SAlan Cox if (m_ret->pool != VM_FREEPOOL_DEFAULT) 148411752d88SAlan Cox vm_phys_set_pool(VM_FREEPOOL_DEFAULT, m_ret, oind); 148511752d88SAlan Cox fl = (*seg->free_queues)[m_ret->pool]; 148611752d88SAlan Cox vm_phys_split_pages(m_ret, oind, fl, order); 14875c1f2cc4SAlan Cox /* Return excess pages to the free lists. */ 14885c1f2cc4SAlan Cox npages_end = roundup2(npages, 1 << imin(oind, order)); 14895c1f2cc4SAlan Cox if (npages < npages_end) 14905c1f2cc4SAlan Cox vm_phys_free_contig(&m_ret[npages], npages_end - npages); 149111752d88SAlan Cox return (m_ret); 149211752d88SAlan Cox } 149311752d88SAlan Cox 149411752d88SAlan Cox #ifdef DDB 149511752d88SAlan Cox /* 149611752d88SAlan Cox * Show the number of physical pages in each of the free lists. 149711752d88SAlan Cox */ 149811752d88SAlan Cox DB_SHOW_COMMAND(freepages, db_show_freepages) 149911752d88SAlan Cox { 150011752d88SAlan Cox struct vm_freelist *fl; 15017e226537SAttilio Rao int flind, oind, pind, dom; 150211752d88SAlan Cox 15037e226537SAttilio Rao for (dom = 0; dom < vm_ndomains; dom++) { 15047e226537SAttilio Rao db_printf("DOMAIN: %d\n", dom); 150511752d88SAlan Cox for (flind = 0; flind < vm_nfreelists; flind++) { 150611752d88SAlan Cox db_printf("FREE LIST %d:\n" 150711752d88SAlan Cox "\n ORDER (SIZE) | NUMBER" 150811752d88SAlan Cox "\n ", flind); 150911752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 151011752d88SAlan Cox db_printf(" | POOL %d", pind); 151111752d88SAlan Cox db_printf("\n-- "); 151211752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 151311752d88SAlan Cox db_printf("-- -- "); 151411752d88SAlan Cox db_printf("--\n"); 151511752d88SAlan Cox for (oind = VM_NFREEORDER - 1; oind >= 0; oind--) { 151611752d88SAlan Cox db_printf(" %2.2d (%6.6dK)", oind, 151711752d88SAlan Cox 1 << (PAGE_SHIFT - 10 + oind)); 151811752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 15197e226537SAttilio Rao fl = vm_phys_free_queues[dom][flind][pind]; 152011752d88SAlan Cox db_printf(" | %6.6d", fl[oind].lcnt); 152111752d88SAlan Cox } 152211752d88SAlan Cox db_printf("\n"); 152311752d88SAlan Cox } 152411752d88SAlan Cox db_printf("\n"); 152511752d88SAlan Cox } 15267e226537SAttilio Rao db_printf("\n"); 15277e226537SAttilio Rao } 152811752d88SAlan Cox } 152911752d88SAlan Cox #endif 1530