111752d88SAlan Cox /*- 2fe267a55SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3fe267a55SPedro F. Giffuni * 411752d88SAlan Cox * Copyright (c) 2002-2006 Rice University 511752d88SAlan Cox * Copyright (c) 2007 Alan L. Cox <alc@cs.rice.edu> 611752d88SAlan Cox * All rights reserved. 711752d88SAlan Cox * 811752d88SAlan Cox * This software was developed for the FreeBSD Project by Alan L. Cox, 911752d88SAlan Cox * Olivier Crameri, Peter Druschel, Sitaram Iyer, and Juan Navarro. 1011752d88SAlan Cox * 1111752d88SAlan Cox * Redistribution and use in source and binary forms, with or without 1211752d88SAlan Cox * modification, are permitted provided that the following conditions 1311752d88SAlan Cox * are met: 1411752d88SAlan Cox * 1. Redistributions of source code must retain the above copyright 1511752d88SAlan Cox * notice, this list of conditions and the following disclaimer. 1611752d88SAlan Cox * 2. Redistributions in binary form must reproduce the above copyright 1711752d88SAlan Cox * notice, this list of conditions and the following disclaimer in the 1811752d88SAlan Cox * documentation and/or other materials provided with the distribution. 1911752d88SAlan Cox * 2011752d88SAlan Cox * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2111752d88SAlan Cox * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2211752d88SAlan Cox * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2311752d88SAlan Cox * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2411752d88SAlan Cox * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 2511752d88SAlan Cox * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2611752d88SAlan Cox * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 2711752d88SAlan Cox * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2811752d88SAlan Cox * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2911752d88SAlan Cox * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 3011752d88SAlan Cox * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3111752d88SAlan Cox * POSSIBILITY OF SUCH DAMAGE. 3211752d88SAlan Cox */ 3311752d88SAlan Cox 34fbd80bd0SAlan Cox /* 35fbd80bd0SAlan Cox * Physical memory system implementation 36fbd80bd0SAlan Cox * 37fbd80bd0SAlan Cox * Any external functions defined by this module are only to be used by the 38fbd80bd0SAlan Cox * virtual memory system. 39fbd80bd0SAlan Cox */ 40fbd80bd0SAlan Cox 4111752d88SAlan Cox #include <sys/cdefs.h> 4211752d88SAlan Cox __FBSDID("$FreeBSD$"); 4311752d88SAlan Cox 4411752d88SAlan Cox #include "opt_ddb.h" 45174b5f38SJohn Baldwin #include "opt_vm.h" 4611752d88SAlan Cox 4711752d88SAlan Cox #include <sys/param.h> 4811752d88SAlan Cox #include <sys/systm.h> 49662e7fa8SMark Johnston #include <sys/domainset.h> 5011752d88SAlan Cox #include <sys/lock.h> 5111752d88SAlan Cox #include <sys/kernel.h> 5211752d88SAlan Cox #include <sys/malloc.h> 5311752d88SAlan Cox #include <sys/mutex.h> 547e226537SAttilio Rao #include <sys/proc.h> 5511752d88SAlan Cox #include <sys/queue.h> 5638d6b2dcSRoger Pau Monné #include <sys/rwlock.h> 5711752d88SAlan Cox #include <sys/sbuf.h> 5811752d88SAlan Cox #include <sys/sysctl.h> 5938d6b2dcSRoger Pau Monné #include <sys/tree.h> 6011752d88SAlan Cox #include <sys/vmmeter.h> 6111752d88SAlan Cox 6211752d88SAlan Cox #include <ddb/ddb.h> 6311752d88SAlan Cox 6411752d88SAlan Cox #include <vm/vm.h> 6511752d88SAlan Cox #include <vm/vm_param.h> 6611752d88SAlan Cox #include <vm/vm_kern.h> 6711752d88SAlan Cox #include <vm/vm_object.h> 6811752d88SAlan Cox #include <vm/vm_page.h> 6911752d88SAlan Cox #include <vm/vm_phys.h> 70e2068d0bSJeff Roberson #include <vm/vm_pagequeue.h> 7111752d88SAlan Cox 72449c2e92SKonstantin Belousov _Static_assert(sizeof(long) * NBBY >= VM_PHYSSEG_MAX, 73449c2e92SKonstantin Belousov "Too many physsegs."); 7411752d88SAlan Cox 75b6715dabSJeff Roberson #ifdef NUMA 76cdfeced8SJeff Roberson struct mem_affinity __read_mostly *mem_affinity; 77cdfeced8SJeff Roberson int __read_mostly *mem_locality; 7862d70a81SJohn Baldwin #endif 79a3870a18SJohn Baldwin 80cdfeced8SJeff Roberson int __read_mostly vm_ndomains = 1; 81463406acSMark Johnston domainset_t __read_mostly all_domains = DOMAINSET_T_INITIALIZER(0x1); 827e226537SAttilio Rao 83cdfeced8SJeff Roberson struct vm_phys_seg __read_mostly vm_phys_segs[VM_PHYSSEG_MAX]; 84cdfeced8SJeff Roberson int __read_mostly vm_phys_nsegs; 8511752d88SAlan Cox 8638d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg; 8738d6b2dcSRoger Pau Monné static int vm_phys_fictitious_cmp(struct vm_phys_fictitious_seg *, 8838d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *); 8938d6b2dcSRoger Pau Monné 9038d6b2dcSRoger Pau Monné RB_HEAD(fict_tree, vm_phys_fictitious_seg) vm_phys_fictitious_tree = 9138d6b2dcSRoger Pau Monné RB_INITIALIZER(_vm_phys_fictitious_tree); 9238d6b2dcSRoger Pau Monné 9338d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg { 9438d6b2dcSRoger Pau Monné RB_ENTRY(vm_phys_fictitious_seg) node; 9538d6b2dcSRoger Pau Monné /* Memory region data */ 96b6de32bdSKonstantin Belousov vm_paddr_t start; 97b6de32bdSKonstantin Belousov vm_paddr_t end; 98b6de32bdSKonstantin Belousov vm_page_t first_page; 9938d6b2dcSRoger Pau Monné }; 10038d6b2dcSRoger Pau Monné 10138d6b2dcSRoger Pau Monné RB_GENERATE_STATIC(fict_tree, vm_phys_fictitious_seg, node, 10238d6b2dcSRoger Pau Monné vm_phys_fictitious_cmp); 10338d6b2dcSRoger Pau Monné 104cdfeced8SJeff Roberson static struct rwlock_padalign vm_phys_fictitious_reg_lock; 105c0432fc3SMark Johnston MALLOC_DEFINE(M_FICT_PAGES, "vm_fictitious", "Fictitious VM pages"); 106b6de32bdSKonstantin Belousov 107cdfeced8SJeff Roberson static struct vm_freelist __aligned(CACHE_LINE_SIZE) 108f2a496d6SKonstantin Belousov vm_phys_free_queues[MAXMEMDOM][VM_NFREELIST][VM_NFREEPOOL] 109f2a496d6SKonstantin Belousov [VM_NFREEORDER_MAX]; 11011752d88SAlan Cox 111cdfeced8SJeff Roberson static int __read_mostly vm_nfreelists; 112d866a563SAlan Cox 113d866a563SAlan Cox /* 114d866a563SAlan Cox * Provides the mapping from VM_FREELIST_* to free list indices (flind). 115d866a563SAlan Cox */ 116cdfeced8SJeff Roberson static int __read_mostly vm_freelist_to_flind[VM_NFREELIST]; 117d866a563SAlan Cox 118d866a563SAlan Cox CTASSERT(VM_FREELIST_DEFAULT == 0); 119d866a563SAlan Cox 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_LOWMEM_BOUNDARY) && defined(VM_DMA32_BOUNDARY) 129d866a563SAlan Cox CTASSERT(VM_LOWMEM_BOUNDARY < VM_DMA32_BOUNDARY); 130d866a563SAlan Cox #endif 13111752d88SAlan Cox 13211752d88SAlan Cox static int sysctl_vm_phys_free(SYSCTL_HANDLER_ARGS); 13311752d88SAlan Cox SYSCTL_OID(_vm, OID_AUTO, phys_free, CTLTYPE_STRING | CTLFLAG_RD, 13411752d88SAlan Cox NULL, 0, sysctl_vm_phys_free, "A", "Phys Free Info"); 13511752d88SAlan Cox 13611752d88SAlan Cox static int sysctl_vm_phys_segs(SYSCTL_HANDLER_ARGS); 13711752d88SAlan Cox SYSCTL_OID(_vm, OID_AUTO, phys_segs, CTLTYPE_STRING | CTLFLAG_RD, 13811752d88SAlan Cox NULL, 0, sysctl_vm_phys_segs, "A", "Phys Seg Info"); 13911752d88SAlan Cox 140b6715dabSJeff Roberson #ifdef NUMA 141415d7ccaSAdrian Chadd static int sysctl_vm_phys_locality(SYSCTL_HANDLER_ARGS); 142415d7ccaSAdrian Chadd SYSCTL_OID(_vm, OID_AUTO, phys_locality, CTLTYPE_STRING | CTLFLAG_RD, 143415d7ccaSAdrian Chadd NULL, 0, sysctl_vm_phys_locality, "A", "Phys Locality Info"); 1446520495aSAdrian Chadd #endif 145415d7ccaSAdrian Chadd 1467e226537SAttilio Rao SYSCTL_INT(_vm, OID_AUTO, ndomains, CTLFLAG_RD, 1477e226537SAttilio Rao &vm_ndomains, 0, "Number of physical memory domains available."); 148a3870a18SJohn Baldwin 149c869e672SAlan Cox static vm_page_t vm_phys_alloc_seg_contig(struct vm_phys_seg *seg, 150c869e672SAlan Cox u_long npages, vm_paddr_t low, vm_paddr_t high, u_long alignment, 151c869e672SAlan Cox vm_paddr_t boundary); 152d866a563SAlan Cox static void _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int domain); 153d866a563SAlan Cox static void vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end); 15411752d88SAlan Cox static void vm_phys_split_pages(vm_page_t m, int oind, struct vm_freelist *fl, 155370a338aSAlan Cox int order, int tail); 15611752d88SAlan Cox 15738d6b2dcSRoger Pau Monné /* 15838d6b2dcSRoger Pau Monné * Red-black tree helpers for vm fictitious range management. 15938d6b2dcSRoger Pau Monné */ 16038d6b2dcSRoger Pau Monné static inline int 16138d6b2dcSRoger Pau Monné vm_phys_fictitious_in_range(struct vm_phys_fictitious_seg *p, 16238d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *range) 16338d6b2dcSRoger Pau Monné { 16438d6b2dcSRoger Pau Monné 16538d6b2dcSRoger Pau Monné KASSERT(range->start != 0 && range->end != 0, 16638d6b2dcSRoger Pau Monné ("Invalid range passed on search for vm_fictitious page")); 16738d6b2dcSRoger Pau Monné if (p->start >= range->end) 16838d6b2dcSRoger Pau Monné return (1); 16938d6b2dcSRoger Pau Monné if (p->start < range->start) 17038d6b2dcSRoger Pau Monné return (-1); 17138d6b2dcSRoger Pau Monné 17238d6b2dcSRoger Pau Monné return (0); 17338d6b2dcSRoger Pau Monné } 17438d6b2dcSRoger Pau Monné 17538d6b2dcSRoger Pau Monné static int 17638d6b2dcSRoger Pau Monné vm_phys_fictitious_cmp(struct vm_phys_fictitious_seg *p1, 17738d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *p2) 17838d6b2dcSRoger Pau Monné { 17938d6b2dcSRoger Pau Monné 18038d6b2dcSRoger Pau Monné /* Check if this is a search for a page */ 18138d6b2dcSRoger Pau Monné if (p1->end == 0) 18238d6b2dcSRoger Pau Monné return (vm_phys_fictitious_in_range(p1, p2)); 18338d6b2dcSRoger Pau Monné 18438d6b2dcSRoger Pau Monné KASSERT(p2->end != 0, 18538d6b2dcSRoger Pau Monné ("Invalid range passed as second parameter to vm fictitious comparison")); 18638d6b2dcSRoger Pau Monné 18738d6b2dcSRoger Pau Monné /* Searching to add a new range */ 18838d6b2dcSRoger Pau Monné if (p1->end <= p2->start) 18938d6b2dcSRoger Pau Monné return (-1); 19038d6b2dcSRoger Pau Monné if (p1->start >= p2->end) 19138d6b2dcSRoger Pau Monné return (1); 19238d6b2dcSRoger Pau Monné 19338d6b2dcSRoger Pau Monné panic("Trying to add overlapping vm fictitious ranges:\n" 19438d6b2dcSRoger Pau Monné "[%#jx:%#jx] and [%#jx:%#jx]", (uintmax_t)p1->start, 19538d6b2dcSRoger Pau Monné (uintmax_t)p1->end, (uintmax_t)p2->start, (uintmax_t)p2->end); 19638d6b2dcSRoger Pau Monné } 19738d6b2dcSRoger Pau Monné 1986f4acaf4SJeff Roberson int 1996f4acaf4SJeff Roberson vm_phys_domain_match(int prefer, vm_paddr_t low, vm_paddr_t high) 200449c2e92SKonstantin Belousov { 201b6715dabSJeff Roberson #ifdef NUMA 2026f4acaf4SJeff Roberson domainset_t mask; 2036f4acaf4SJeff Roberson int i; 204449c2e92SKonstantin Belousov 2056f4acaf4SJeff Roberson if (vm_ndomains == 1 || mem_affinity == NULL) 2066f4acaf4SJeff Roberson return (0); 2076f4acaf4SJeff Roberson 2086f4acaf4SJeff Roberson DOMAINSET_ZERO(&mask); 2096f4acaf4SJeff Roberson /* 2106f4acaf4SJeff Roberson * Check for any memory that overlaps low, high. 2116f4acaf4SJeff Roberson */ 2126f4acaf4SJeff Roberson for (i = 0; mem_affinity[i].end != 0; i++) 2136f4acaf4SJeff Roberson if (mem_affinity[i].start <= high && 2146f4acaf4SJeff Roberson mem_affinity[i].end >= low) 2156f4acaf4SJeff Roberson DOMAINSET_SET(mem_affinity[i].domain, &mask); 2166f4acaf4SJeff Roberson if (prefer != -1 && DOMAINSET_ISSET(prefer, &mask)) 2176f4acaf4SJeff Roberson return (prefer); 2186f4acaf4SJeff Roberson if (DOMAINSET_EMPTY(&mask)) 2196f4acaf4SJeff Roberson panic("vm_phys_domain_match: Impossible constraint"); 2206f4acaf4SJeff Roberson return (DOMAINSET_FFS(&mask) - 1); 2216f4acaf4SJeff Roberson #else 2226f4acaf4SJeff Roberson return (0); 2236f4acaf4SJeff Roberson #endif 224449c2e92SKonstantin Belousov } 225449c2e92SKonstantin Belousov 22611752d88SAlan Cox /* 22711752d88SAlan Cox * Outputs the state of the physical memory allocator, specifically, 22811752d88SAlan Cox * the amount of physical memory in each free list. 22911752d88SAlan Cox */ 23011752d88SAlan Cox static int 23111752d88SAlan Cox sysctl_vm_phys_free(SYSCTL_HANDLER_ARGS) 23211752d88SAlan Cox { 23311752d88SAlan Cox struct sbuf sbuf; 23411752d88SAlan Cox struct vm_freelist *fl; 2357e226537SAttilio Rao int dom, error, flind, oind, pind; 23611752d88SAlan Cox 23700f0e671SMatthew D Fleming error = sysctl_wire_old_buffer(req, 0); 23800f0e671SMatthew D Fleming if (error != 0) 23900f0e671SMatthew D Fleming return (error); 2407e226537SAttilio Rao sbuf_new_for_sysctl(&sbuf, NULL, 128 * vm_ndomains, req); 2417e226537SAttilio Rao for (dom = 0; dom < vm_ndomains; dom++) { 242eb2f42fbSAlan Cox sbuf_printf(&sbuf,"\nDOMAIN %d:\n", dom); 24311752d88SAlan Cox for (flind = 0; flind < vm_nfreelists; flind++) { 244eb2f42fbSAlan Cox sbuf_printf(&sbuf, "\nFREE LIST %d:\n" 24511752d88SAlan Cox "\n ORDER (SIZE) | NUMBER" 24611752d88SAlan Cox "\n ", flind); 24711752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 24811752d88SAlan Cox sbuf_printf(&sbuf, " | POOL %d", pind); 24911752d88SAlan Cox sbuf_printf(&sbuf, "\n-- "); 25011752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 25111752d88SAlan Cox sbuf_printf(&sbuf, "-- -- "); 25211752d88SAlan Cox sbuf_printf(&sbuf, "--\n"); 25311752d88SAlan Cox for (oind = VM_NFREEORDER - 1; oind >= 0; oind--) { 254d689bc00SAlan Cox sbuf_printf(&sbuf, " %2d (%6dK)", oind, 25511752d88SAlan Cox 1 << (PAGE_SHIFT - 10 + oind)); 25611752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 2577e226537SAttilio Rao fl = vm_phys_free_queues[dom][flind][pind]; 258eb2f42fbSAlan Cox sbuf_printf(&sbuf, " | %6d", 2597e226537SAttilio Rao fl[oind].lcnt); 26011752d88SAlan Cox } 26111752d88SAlan Cox sbuf_printf(&sbuf, "\n"); 26211752d88SAlan Cox } 2637e226537SAttilio Rao } 26411752d88SAlan Cox } 2654e657159SMatthew D Fleming error = sbuf_finish(&sbuf); 26611752d88SAlan Cox sbuf_delete(&sbuf); 26711752d88SAlan Cox return (error); 26811752d88SAlan Cox } 26911752d88SAlan Cox 27011752d88SAlan Cox /* 27111752d88SAlan Cox * Outputs the set of physical memory segments. 27211752d88SAlan Cox */ 27311752d88SAlan Cox static int 27411752d88SAlan Cox sysctl_vm_phys_segs(SYSCTL_HANDLER_ARGS) 27511752d88SAlan Cox { 27611752d88SAlan Cox struct sbuf sbuf; 27711752d88SAlan Cox struct vm_phys_seg *seg; 27811752d88SAlan Cox int error, segind; 27911752d88SAlan Cox 28000f0e671SMatthew D Fleming error = sysctl_wire_old_buffer(req, 0); 28100f0e671SMatthew D Fleming if (error != 0) 28200f0e671SMatthew D Fleming return (error); 2834e657159SMatthew D Fleming sbuf_new_for_sysctl(&sbuf, NULL, 128, req); 28411752d88SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 28511752d88SAlan Cox sbuf_printf(&sbuf, "\nSEGMENT %d:\n\n", segind); 28611752d88SAlan Cox seg = &vm_phys_segs[segind]; 28711752d88SAlan Cox sbuf_printf(&sbuf, "start: %#jx\n", 28811752d88SAlan Cox (uintmax_t)seg->start); 28911752d88SAlan Cox sbuf_printf(&sbuf, "end: %#jx\n", 29011752d88SAlan Cox (uintmax_t)seg->end); 291a3870a18SJohn Baldwin sbuf_printf(&sbuf, "domain: %d\n", seg->domain); 29211752d88SAlan Cox sbuf_printf(&sbuf, "free list: %p\n", seg->free_queues); 29311752d88SAlan Cox } 2944e657159SMatthew D Fleming error = sbuf_finish(&sbuf); 29511752d88SAlan Cox sbuf_delete(&sbuf); 29611752d88SAlan Cox return (error); 29711752d88SAlan Cox } 29811752d88SAlan Cox 299415d7ccaSAdrian Chadd /* 300415d7ccaSAdrian Chadd * Return affinity, or -1 if there's no affinity information. 301415d7ccaSAdrian Chadd */ 3026520495aSAdrian Chadd int 303415d7ccaSAdrian Chadd vm_phys_mem_affinity(int f, int t) 304415d7ccaSAdrian Chadd { 305415d7ccaSAdrian Chadd 306b6715dabSJeff Roberson #ifdef NUMA 307415d7ccaSAdrian Chadd if (mem_locality == NULL) 308415d7ccaSAdrian Chadd return (-1); 309415d7ccaSAdrian Chadd if (f >= vm_ndomains || t >= vm_ndomains) 310415d7ccaSAdrian Chadd return (-1); 311415d7ccaSAdrian Chadd return (mem_locality[f * vm_ndomains + t]); 3126520495aSAdrian Chadd #else 3136520495aSAdrian Chadd return (-1); 3146520495aSAdrian Chadd #endif 315415d7ccaSAdrian Chadd } 316415d7ccaSAdrian Chadd 317b6715dabSJeff Roberson #ifdef NUMA 318415d7ccaSAdrian Chadd /* 319415d7ccaSAdrian Chadd * Outputs the VM locality table. 320415d7ccaSAdrian Chadd */ 321415d7ccaSAdrian Chadd static int 322415d7ccaSAdrian Chadd sysctl_vm_phys_locality(SYSCTL_HANDLER_ARGS) 323415d7ccaSAdrian Chadd { 324415d7ccaSAdrian Chadd struct sbuf sbuf; 325415d7ccaSAdrian Chadd int error, i, j; 326415d7ccaSAdrian Chadd 327415d7ccaSAdrian Chadd error = sysctl_wire_old_buffer(req, 0); 328415d7ccaSAdrian Chadd if (error != 0) 329415d7ccaSAdrian Chadd return (error); 330415d7ccaSAdrian Chadd sbuf_new_for_sysctl(&sbuf, NULL, 128, req); 331415d7ccaSAdrian Chadd 332415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "\n"); 333415d7ccaSAdrian Chadd 334415d7ccaSAdrian Chadd for (i = 0; i < vm_ndomains; i++) { 335415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "%d: ", i); 336415d7ccaSAdrian Chadd for (j = 0; j < vm_ndomains; j++) { 337415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "%d ", vm_phys_mem_affinity(i, j)); 338415d7ccaSAdrian Chadd } 339415d7ccaSAdrian Chadd sbuf_printf(&sbuf, "\n"); 340415d7ccaSAdrian Chadd } 341415d7ccaSAdrian Chadd error = sbuf_finish(&sbuf); 342415d7ccaSAdrian Chadd sbuf_delete(&sbuf); 343415d7ccaSAdrian Chadd return (error); 344415d7ccaSAdrian Chadd } 3456520495aSAdrian Chadd #endif 346415d7ccaSAdrian Chadd 3477e226537SAttilio Rao static void 3487e226537SAttilio Rao vm_freelist_add(struct vm_freelist *fl, vm_page_t m, int order, int tail) 349a3870a18SJohn Baldwin { 350a3870a18SJohn Baldwin 3517e226537SAttilio Rao m->order = order; 3527e226537SAttilio Rao if (tail) 3535cd29d0fSMark Johnston TAILQ_INSERT_TAIL(&fl[order].pl, m, listq); 3547e226537SAttilio Rao else 3555cd29d0fSMark Johnston TAILQ_INSERT_HEAD(&fl[order].pl, m, listq); 3567e226537SAttilio Rao fl[order].lcnt++; 357a3870a18SJohn Baldwin } 3587e226537SAttilio Rao 3597e226537SAttilio Rao static void 3607e226537SAttilio Rao vm_freelist_rem(struct vm_freelist *fl, vm_page_t m, int order) 3617e226537SAttilio Rao { 3627e226537SAttilio Rao 3635cd29d0fSMark Johnston TAILQ_REMOVE(&fl[order].pl, m, listq); 3647e226537SAttilio Rao fl[order].lcnt--; 3657e226537SAttilio Rao m->order = VM_NFREEORDER; 366a3870a18SJohn Baldwin } 367a3870a18SJohn Baldwin 36811752d88SAlan Cox /* 36911752d88SAlan Cox * Create a physical memory segment. 37011752d88SAlan Cox */ 37111752d88SAlan Cox static void 372d866a563SAlan Cox _vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end, int domain) 37311752d88SAlan Cox { 37411752d88SAlan Cox struct vm_phys_seg *seg; 37511752d88SAlan Cox 37611752d88SAlan Cox KASSERT(vm_phys_nsegs < VM_PHYSSEG_MAX, 37711752d88SAlan Cox ("vm_phys_create_seg: increase VM_PHYSSEG_MAX")); 378ef435ae7SJeff Roberson KASSERT(domain >= 0 && domain < vm_ndomains, 3797e226537SAttilio Rao ("vm_phys_create_seg: invalid domain provided")); 38011752d88SAlan Cox seg = &vm_phys_segs[vm_phys_nsegs++]; 381271f0f12SAlan Cox while (seg > vm_phys_segs && (seg - 1)->start >= end) { 382271f0f12SAlan Cox *seg = *(seg - 1); 383271f0f12SAlan Cox seg--; 384271f0f12SAlan Cox } 38511752d88SAlan Cox seg->start = start; 38611752d88SAlan Cox seg->end = end; 387a3870a18SJohn Baldwin seg->domain = domain; 38811752d88SAlan Cox } 38911752d88SAlan Cox 390a3870a18SJohn Baldwin static void 391d866a563SAlan Cox vm_phys_create_seg(vm_paddr_t start, vm_paddr_t end) 392a3870a18SJohn Baldwin { 393b6715dabSJeff Roberson #ifdef NUMA 394a3870a18SJohn Baldwin int i; 395a3870a18SJohn Baldwin 396a3870a18SJohn Baldwin if (mem_affinity == NULL) { 397d866a563SAlan Cox _vm_phys_create_seg(start, end, 0); 398a3870a18SJohn Baldwin return; 399a3870a18SJohn Baldwin } 400a3870a18SJohn Baldwin 401a3870a18SJohn Baldwin for (i = 0;; i++) { 402a3870a18SJohn Baldwin if (mem_affinity[i].end == 0) 403a3870a18SJohn Baldwin panic("Reached end of affinity info"); 404a3870a18SJohn Baldwin if (mem_affinity[i].end <= start) 405a3870a18SJohn Baldwin continue; 406a3870a18SJohn Baldwin if (mem_affinity[i].start > start) 407a3870a18SJohn Baldwin panic("No affinity info for start %jx", 408a3870a18SJohn Baldwin (uintmax_t)start); 409a3870a18SJohn Baldwin if (mem_affinity[i].end >= end) { 410d866a563SAlan Cox _vm_phys_create_seg(start, end, 411a3870a18SJohn Baldwin mem_affinity[i].domain); 412a3870a18SJohn Baldwin break; 413a3870a18SJohn Baldwin } 414d866a563SAlan Cox _vm_phys_create_seg(start, mem_affinity[i].end, 415a3870a18SJohn Baldwin mem_affinity[i].domain); 416a3870a18SJohn Baldwin start = mem_affinity[i].end; 417a3870a18SJohn Baldwin } 41862d70a81SJohn Baldwin #else 41962d70a81SJohn Baldwin _vm_phys_create_seg(start, end, 0); 42062d70a81SJohn Baldwin #endif 421a3870a18SJohn Baldwin } 422a3870a18SJohn Baldwin 42311752d88SAlan Cox /* 424271f0f12SAlan Cox * Add a physical memory segment. 425271f0f12SAlan Cox */ 426271f0f12SAlan Cox void 427271f0f12SAlan Cox vm_phys_add_seg(vm_paddr_t start, vm_paddr_t end) 428271f0f12SAlan Cox { 429d866a563SAlan Cox vm_paddr_t paddr; 430271f0f12SAlan Cox 431271f0f12SAlan Cox KASSERT((start & PAGE_MASK) == 0, 432271f0f12SAlan Cox ("vm_phys_define_seg: start is not page aligned")); 433271f0f12SAlan Cox KASSERT((end & PAGE_MASK) == 0, 434271f0f12SAlan Cox ("vm_phys_define_seg: end is not page aligned")); 435d866a563SAlan Cox 436d866a563SAlan Cox /* 437d866a563SAlan Cox * Split the physical memory segment if it spans two or more free 438d866a563SAlan Cox * list boundaries. 439d866a563SAlan Cox */ 440d866a563SAlan Cox paddr = start; 441d866a563SAlan Cox #ifdef VM_FREELIST_LOWMEM 442d866a563SAlan Cox if (paddr < VM_LOWMEM_BOUNDARY && end > VM_LOWMEM_BOUNDARY) { 443d866a563SAlan Cox vm_phys_create_seg(paddr, VM_LOWMEM_BOUNDARY); 444d866a563SAlan Cox paddr = VM_LOWMEM_BOUNDARY; 445d866a563SAlan Cox } 446271f0f12SAlan Cox #endif 447d866a563SAlan Cox #ifdef VM_FREELIST_DMA32 448d866a563SAlan Cox if (paddr < VM_DMA32_BOUNDARY && end > VM_DMA32_BOUNDARY) { 449d866a563SAlan Cox vm_phys_create_seg(paddr, VM_DMA32_BOUNDARY); 450d866a563SAlan Cox paddr = VM_DMA32_BOUNDARY; 451d866a563SAlan Cox } 452d866a563SAlan Cox #endif 453d866a563SAlan Cox vm_phys_create_seg(paddr, end); 454271f0f12SAlan Cox } 455271f0f12SAlan Cox 456271f0f12SAlan Cox /* 45711752d88SAlan Cox * Initialize the physical memory allocator. 458d866a563SAlan Cox * 459d866a563SAlan Cox * Requires that vm_page_array is initialized! 46011752d88SAlan Cox */ 46111752d88SAlan Cox void 46211752d88SAlan Cox vm_phys_init(void) 46311752d88SAlan Cox { 46411752d88SAlan Cox struct vm_freelist *fl; 46572aebdd7SAlan Cox struct vm_phys_seg *end_seg, *prev_seg, *seg, *tmp_seg; 466d866a563SAlan Cox u_long npages; 467d866a563SAlan Cox int dom, flind, freelist, oind, pind, segind; 46811752d88SAlan Cox 469d866a563SAlan Cox /* 470d866a563SAlan Cox * Compute the number of free lists, and generate the mapping from the 471d866a563SAlan Cox * manifest constants VM_FREELIST_* to the free list indices. 472d866a563SAlan Cox * 473d866a563SAlan Cox * Initially, the entries of vm_freelist_to_flind[] are set to either 474d866a563SAlan Cox * 0 or 1 to indicate which free lists should be created. 475d866a563SAlan Cox */ 476d866a563SAlan Cox npages = 0; 477d866a563SAlan Cox for (segind = vm_phys_nsegs - 1; segind >= 0; segind--) { 478d866a563SAlan Cox seg = &vm_phys_segs[segind]; 479d866a563SAlan Cox #ifdef VM_FREELIST_LOWMEM 480d866a563SAlan Cox if (seg->end <= VM_LOWMEM_BOUNDARY) 481d866a563SAlan Cox vm_freelist_to_flind[VM_FREELIST_LOWMEM] = 1; 482d866a563SAlan Cox else 483d866a563SAlan Cox #endif 484d866a563SAlan Cox #ifdef VM_FREELIST_DMA32 485d866a563SAlan Cox if ( 486d866a563SAlan Cox #ifdef VM_DMA32_NPAGES_THRESHOLD 487d866a563SAlan Cox /* 488d866a563SAlan Cox * Create the DMA32 free list only if the amount of 489d866a563SAlan Cox * physical memory above physical address 4G exceeds the 490d866a563SAlan Cox * given threshold. 491d866a563SAlan Cox */ 492d866a563SAlan Cox npages > VM_DMA32_NPAGES_THRESHOLD && 493d866a563SAlan Cox #endif 494d866a563SAlan Cox seg->end <= VM_DMA32_BOUNDARY) 495d866a563SAlan Cox vm_freelist_to_flind[VM_FREELIST_DMA32] = 1; 496d866a563SAlan Cox else 497d866a563SAlan Cox #endif 498d866a563SAlan Cox { 499d866a563SAlan Cox npages += atop(seg->end - seg->start); 500d866a563SAlan Cox vm_freelist_to_flind[VM_FREELIST_DEFAULT] = 1; 501d866a563SAlan Cox } 502d866a563SAlan Cox } 503d866a563SAlan Cox /* Change each entry into a running total of the free lists. */ 504d866a563SAlan Cox for (freelist = 1; freelist < VM_NFREELIST; freelist++) { 505d866a563SAlan Cox vm_freelist_to_flind[freelist] += 506d866a563SAlan Cox vm_freelist_to_flind[freelist - 1]; 507d866a563SAlan Cox } 508d866a563SAlan Cox vm_nfreelists = vm_freelist_to_flind[VM_NFREELIST - 1]; 509d866a563SAlan Cox KASSERT(vm_nfreelists > 0, ("vm_phys_init: no free lists")); 510d866a563SAlan Cox /* Change each entry into a free list index. */ 511d866a563SAlan Cox for (freelist = 0; freelist < VM_NFREELIST; freelist++) 512d866a563SAlan Cox vm_freelist_to_flind[freelist]--; 513d866a563SAlan Cox 514d866a563SAlan Cox /* 515d866a563SAlan Cox * Initialize the first_page and free_queues fields of each physical 516d866a563SAlan Cox * memory segment. 517d866a563SAlan Cox */ 518271f0f12SAlan Cox #ifdef VM_PHYSSEG_SPARSE 519d866a563SAlan Cox npages = 0; 52011752d88SAlan Cox #endif 521271f0f12SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 522271f0f12SAlan Cox seg = &vm_phys_segs[segind]; 523271f0f12SAlan Cox #ifdef VM_PHYSSEG_SPARSE 524d866a563SAlan Cox seg->first_page = &vm_page_array[npages]; 525d866a563SAlan Cox npages += atop(seg->end - seg->start); 526271f0f12SAlan Cox #else 527271f0f12SAlan Cox seg->first_page = PHYS_TO_VM_PAGE(seg->start); 52811752d88SAlan Cox #endif 529d866a563SAlan Cox #ifdef VM_FREELIST_LOWMEM 530d866a563SAlan Cox if (seg->end <= VM_LOWMEM_BOUNDARY) { 531d866a563SAlan Cox flind = vm_freelist_to_flind[VM_FREELIST_LOWMEM]; 532d866a563SAlan Cox KASSERT(flind >= 0, 533d866a563SAlan Cox ("vm_phys_init: LOWMEM flind < 0")); 534d866a563SAlan Cox } else 535d866a563SAlan Cox #endif 536d866a563SAlan Cox #ifdef VM_FREELIST_DMA32 537d866a563SAlan Cox if (seg->end <= VM_DMA32_BOUNDARY) { 538d866a563SAlan Cox flind = vm_freelist_to_flind[VM_FREELIST_DMA32]; 539d866a563SAlan Cox KASSERT(flind >= 0, 540d866a563SAlan Cox ("vm_phys_init: DMA32 flind < 0")); 541d866a563SAlan Cox } else 542d866a563SAlan Cox #endif 543d866a563SAlan Cox { 544d866a563SAlan Cox flind = vm_freelist_to_flind[VM_FREELIST_DEFAULT]; 545d866a563SAlan Cox KASSERT(flind >= 0, 546d866a563SAlan Cox ("vm_phys_init: DEFAULT flind < 0")); 54711752d88SAlan Cox } 548d866a563SAlan Cox seg->free_queues = &vm_phys_free_queues[seg->domain][flind]; 549d866a563SAlan Cox } 550d866a563SAlan Cox 551d866a563SAlan Cox /* 55272aebdd7SAlan Cox * Coalesce physical memory segments that are contiguous and share the 55372aebdd7SAlan Cox * same per-domain free queues. 55472aebdd7SAlan Cox */ 55572aebdd7SAlan Cox prev_seg = vm_phys_segs; 55672aebdd7SAlan Cox seg = &vm_phys_segs[1]; 55772aebdd7SAlan Cox end_seg = &vm_phys_segs[vm_phys_nsegs]; 55872aebdd7SAlan Cox while (seg < end_seg) { 55972aebdd7SAlan Cox if (prev_seg->end == seg->start && 56072aebdd7SAlan Cox prev_seg->free_queues == seg->free_queues) { 56172aebdd7SAlan Cox prev_seg->end = seg->end; 56272aebdd7SAlan Cox KASSERT(prev_seg->domain == seg->domain, 56372aebdd7SAlan Cox ("vm_phys_init: free queues cannot span domains")); 56472aebdd7SAlan Cox vm_phys_nsegs--; 56572aebdd7SAlan Cox end_seg--; 56672aebdd7SAlan Cox for (tmp_seg = seg; tmp_seg < end_seg; tmp_seg++) 56772aebdd7SAlan Cox *tmp_seg = *(tmp_seg + 1); 56872aebdd7SAlan Cox } else { 56972aebdd7SAlan Cox prev_seg = seg; 57072aebdd7SAlan Cox seg++; 57172aebdd7SAlan Cox } 57272aebdd7SAlan Cox } 57372aebdd7SAlan Cox 57472aebdd7SAlan Cox /* 575d866a563SAlan Cox * Initialize the free queues. 576d866a563SAlan Cox */ 5777e226537SAttilio Rao for (dom = 0; dom < vm_ndomains; dom++) { 57811752d88SAlan Cox for (flind = 0; flind < vm_nfreelists; flind++) { 57911752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 5807e226537SAttilio Rao fl = vm_phys_free_queues[dom][flind][pind]; 58111752d88SAlan Cox for (oind = 0; oind < VM_NFREEORDER; oind++) 58211752d88SAlan Cox TAILQ_INIT(&fl[oind].pl); 58311752d88SAlan Cox } 58411752d88SAlan Cox } 585a3870a18SJohn Baldwin } 586d866a563SAlan Cox 58738d6b2dcSRoger Pau Monné rw_init(&vm_phys_fictitious_reg_lock, "vmfctr"); 58811752d88SAlan Cox } 58911752d88SAlan Cox 59011752d88SAlan Cox /* 591662e7fa8SMark Johnston * Register info about the NUMA topology of the system. 592662e7fa8SMark Johnston * 593662e7fa8SMark Johnston * Invoked by platform-dependent code prior to vm_phys_init(). 594662e7fa8SMark Johnston */ 595662e7fa8SMark Johnston void 596662e7fa8SMark Johnston vm_phys_register_domains(int ndomains, struct mem_affinity *affinity, 597662e7fa8SMark Johnston int *locality) 598662e7fa8SMark Johnston { 599662e7fa8SMark Johnston #ifdef NUMA 600b61f3142SMark Johnston int d, i; 601662e7fa8SMark Johnston 602b61f3142SMark Johnston /* 603b61f3142SMark Johnston * For now the only override value that we support is 1, which 604b61f3142SMark Johnston * effectively disables NUMA-awareness in the allocators. 605b61f3142SMark Johnston */ 606b61f3142SMark Johnston d = 0; 607b61f3142SMark Johnston TUNABLE_INT_FETCH("vm.numa.disabled", &d); 608b61f3142SMark Johnston if (d) 609b61f3142SMark Johnston ndomains = 1; 610b61f3142SMark Johnston 611b61f3142SMark Johnston if (ndomains > 1) { 612662e7fa8SMark Johnston vm_ndomains = ndomains; 613662e7fa8SMark Johnston mem_affinity = affinity; 614662e7fa8SMark Johnston mem_locality = locality; 615b61f3142SMark Johnston } 616662e7fa8SMark Johnston 617662e7fa8SMark Johnston for (i = 0; i < vm_ndomains; i++) 618662e7fa8SMark Johnston DOMAINSET_SET(i, &all_domains); 619662e7fa8SMark Johnston #else 620662e7fa8SMark Johnston (void)ndomains; 621662e7fa8SMark Johnston (void)affinity; 622662e7fa8SMark Johnston (void)locality; 623662e7fa8SMark Johnston #endif 624662e7fa8SMark Johnston } 625662e7fa8SMark Johnston 626662e7fa8SMark Johnston /* 62711752d88SAlan Cox * Split a contiguous, power of two-sized set of physical pages. 628370a338aSAlan Cox * 629370a338aSAlan Cox * When this function is called by a page allocation function, the caller 630370a338aSAlan Cox * should request insertion at the head unless the order [order, oind) queues 631370a338aSAlan Cox * are known to be empty. The objective being to reduce the likelihood of 632370a338aSAlan Cox * long-term fragmentation by promoting contemporaneous allocation and 633370a338aSAlan Cox * (hopefully) deallocation. 63411752d88SAlan Cox */ 63511752d88SAlan Cox static __inline void 636370a338aSAlan Cox vm_phys_split_pages(vm_page_t m, int oind, struct vm_freelist *fl, int order, 637370a338aSAlan Cox int tail) 63811752d88SAlan Cox { 63911752d88SAlan Cox vm_page_t m_buddy; 64011752d88SAlan Cox 64111752d88SAlan Cox while (oind > order) { 64211752d88SAlan Cox oind--; 64311752d88SAlan Cox m_buddy = &m[1 << oind]; 64411752d88SAlan Cox KASSERT(m_buddy->order == VM_NFREEORDER, 64511752d88SAlan Cox ("vm_phys_split_pages: page %p has unexpected order %d", 64611752d88SAlan Cox m_buddy, m_buddy->order)); 647370a338aSAlan Cox vm_freelist_add(fl, m_buddy, oind, tail); 64811752d88SAlan Cox } 64911752d88SAlan Cox } 65011752d88SAlan Cox 65111752d88SAlan Cox /* 6527493904eSAlan Cox * Add the physical pages [m, m + npages) at the end of a power-of-two aligned 6537493904eSAlan Cox * and sized set to the specified free list. 6547493904eSAlan Cox * 6557493904eSAlan Cox * When this function is called by a page allocation function, the caller 6567493904eSAlan Cox * should request insertion at the head unless the lower-order queues are 6577493904eSAlan Cox * known to be empty. The objective being to reduce the likelihood of long- 6587493904eSAlan Cox * term fragmentation by promoting contemporaneous allocation and (hopefully) 6597493904eSAlan Cox * deallocation. 6607493904eSAlan Cox * 6617493904eSAlan Cox * The physical page m's buddy must not be free. 6627493904eSAlan Cox */ 6637493904eSAlan Cox static void 6647493904eSAlan Cox vm_phys_enq_range(vm_page_t m, u_int npages, struct vm_freelist *fl, int tail) 6657493904eSAlan Cox { 6667493904eSAlan Cox u_int n; 6677493904eSAlan Cox int order; 6687493904eSAlan Cox 6697493904eSAlan Cox KASSERT(npages > 0, ("vm_phys_enq_range: npages is 0")); 6707493904eSAlan Cox KASSERT(((VM_PAGE_TO_PHYS(m) + npages * PAGE_SIZE) & 6717493904eSAlan Cox ((PAGE_SIZE << (fls(npages) - 1)) - 1)) == 0, 6727493904eSAlan Cox ("vm_phys_enq_range: page %p and npages %u are misaligned", 6737493904eSAlan Cox m, npages)); 6747493904eSAlan Cox do { 6757493904eSAlan Cox KASSERT(m->order == VM_NFREEORDER, 6767493904eSAlan Cox ("vm_phys_enq_range: page %p has unexpected order %d", 6777493904eSAlan Cox m, m->order)); 6787493904eSAlan Cox order = ffs(npages) - 1; 6797493904eSAlan Cox KASSERT(order < VM_NFREEORDER, 6807493904eSAlan Cox ("vm_phys_enq_range: order %d is out of range", order)); 6817493904eSAlan Cox vm_freelist_add(fl, m, order, tail); 6827493904eSAlan Cox n = 1 << order; 6837493904eSAlan Cox m += n; 6847493904eSAlan Cox npages -= n; 6857493904eSAlan Cox } while (npages > 0); 6867493904eSAlan Cox } 6877493904eSAlan Cox 6887493904eSAlan Cox /* 68989ea39a7SAlan Cox * Tries to allocate the specified number of pages from the specified pool 69089ea39a7SAlan Cox * within the specified domain. Returns the actual number of allocated pages 69189ea39a7SAlan Cox * and a pointer to each page through the array ma[]. 69289ea39a7SAlan Cox * 69332d81f21SAlan Cox * The returned pages may not be physically contiguous. However, in contrast 69432d81f21SAlan Cox * to performing multiple, back-to-back calls to vm_phys_alloc_pages(..., 0), 69532d81f21SAlan Cox * calling this function once to allocate the desired number of pages will 69632d81f21SAlan Cox * avoid wasted time in vm_phys_split_pages(). 69789ea39a7SAlan Cox * 69889ea39a7SAlan Cox * The free page queues for the specified domain must be locked. 69989ea39a7SAlan Cox */ 70089ea39a7SAlan Cox int 70189ea39a7SAlan Cox vm_phys_alloc_npages(int domain, int pool, int npages, vm_page_t ma[]) 70289ea39a7SAlan Cox { 70389ea39a7SAlan Cox struct vm_freelist *alt, *fl; 70489ea39a7SAlan Cox vm_page_t m; 70589ea39a7SAlan Cox int avail, end, flind, freelist, i, need, oind, pind; 70689ea39a7SAlan Cox 70789ea39a7SAlan Cox KASSERT(domain >= 0 && domain < vm_ndomains, 70889ea39a7SAlan Cox ("vm_phys_alloc_npages: domain %d is out of range", domain)); 70989ea39a7SAlan Cox KASSERT(pool < VM_NFREEPOOL, 71089ea39a7SAlan Cox ("vm_phys_alloc_npages: pool %d is out of range", pool)); 71189ea39a7SAlan Cox KASSERT(npages <= 1 << (VM_NFREEORDER - 1), 71289ea39a7SAlan Cox ("vm_phys_alloc_npages: npages %d is out of range", npages)); 71389ea39a7SAlan Cox vm_domain_free_assert_locked(VM_DOMAIN(domain)); 71489ea39a7SAlan Cox i = 0; 71589ea39a7SAlan Cox for (freelist = 0; freelist < VM_NFREELIST; freelist++) { 71689ea39a7SAlan Cox flind = vm_freelist_to_flind[freelist]; 71789ea39a7SAlan Cox if (flind < 0) 71889ea39a7SAlan Cox continue; 71989ea39a7SAlan Cox fl = vm_phys_free_queues[domain][flind][pool]; 72089ea39a7SAlan Cox for (oind = 0; oind < VM_NFREEORDER; oind++) { 72189ea39a7SAlan Cox while ((m = TAILQ_FIRST(&fl[oind].pl)) != NULL) { 72289ea39a7SAlan Cox vm_freelist_rem(fl, m, oind); 72389ea39a7SAlan Cox avail = 1 << oind; 72489ea39a7SAlan Cox need = imin(npages - i, avail); 72589ea39a7SAlan Cox for (end = i + need; i < end;) 72689ea39a7SAlan Cox ma[i++] = m++; 72789ea39a7SAlan Cox if (need < avail) { 7287493904eSAlan Cox /* 7297493904eSAlan Cox * Return excess pages to fl. Its 7307493904eSAlan Cox * order [0, oind) queues are empty. 7317493904eSAlan Cox */ 7327493904eSAlan Cox vm_phys_enq_range(m, avail - need, fl, 7337493904eSAlan Cox 1); 73489ea39a7SAlan Cox return (npages); 73589ea39a7SAlan Cox } else if (i == npages) 73689ea39a7SAlan Cox return (npages); 73789ea39a7SAlan Cox } 73889ea39a7SAlan Cox } 73989ea39a7SAlan Cox for (oind = VM_NFREEORDER - 1; oind >= 0; oind--) { 74089ea39a7SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 74189ea39a7SAlan Cox alt = vm_phys_free_queues[domain][flind][pind]; 74289ea39a7SAlan Cox while ((m = TAILQ_FIRST(&alt[oind].pl)) != 74389ea39a7SAlan Cox NULL) { 74489ea39a7SAlan Cox vm_freelist_rem(alt, m, oind); 74589ea39a7SAlan Cox vm_phys_set_pool(pool, m, oind); 74689ea39a7SAlan Cox avail = 1 << oind; 74789ea39a7SAlan Cox need = imin(npages - i, avail); 74889ea39a7SAlan Cox for (end = i + need; i < end;) 74989ea39a7SAlan Cox ma[i++] = m++; 75089ea39a7SAlan Cox if (need < avail) { 7517493904eSAlan Cox /* 7527493904eSAlan Cox * Return excess pages to fl. 7537493904eSAlan Cox * Its order [0, oind) queues 7547493904eSAlan Cox * are empty. 7557493904eSAlan Cox */ 7567493904eSAlan Cox vm_phys_enq_range(m, avail - 7577493904eSAlan Cox need, fl, 1); 75889ea39a7SAlan Cox return (npages); 75989ea39a7SAlan Cox } else if (i == npages) 76089ea39a7SAlan Cox return (npages); 76189ea39a7SAlan Cox } 76289ea39a7SAlan Cox } 76389ea39a7SAlan Cox } 76489ea39a7SAlan Cox } 76589ea39a7SAlan Cox return (i); 76689ea39a7SAlan Cox } 76789ea39a7SAlan Cox 76889ea39a7SAlan Cox /* 76911752d88SAlan Cox * Allocate a contiguous, power of two-sized set of physical pages 77011752d88SAlan Cox * from the free lists. 7718941dc44SAlan Cox * 7728941dc44SAlan Cox * The free page queues must be locked. 77311752d88SAlan Cox */ 77411752d88SAlan Cox vm_page_t 775ef435ae7SJeff Roberson vm_phys_alloc_pages(int domain, int pool, int order) 77611752d88SAlan Cox { 77749ca10d4SJayachandran C. vm_page_t m; 7780db2102aSMichael Zhilin int freelist; 77949ca10d4SJayachandran C. 7800db2102aSMichael Zhilin for (freelist = 0; freelist < VM_NFREELIST; freelist++) { 7810db2102aSMichael Zhilin m = vm_phys_alloc_freelist_pages(domain, freelist, pool, order); 78249ca10d4SJayachandran C. if (m != NULL) 78349ca10d4SJayachandran C. return (m); 78449ca10d4SJayachandran C. } 78549ca10d4SJayachandran C. return (NULL); 78649ca10d4SJayachandran C. } 78749ca10d4SJayachandran C. 78849ca10d4SJayachandran C. /* 789d866a563SAlan Cox * Allocate a contiguous, power of two-sized set of physical pages from the 790d866a563SAlan Cox * specified free list. The free list must be specified using one of the 791d866a563SAlan Cox * manifest constants VM_FREELIST_*. 792d866a563SAlan Cox * 793d866a563SAlan Cox * The free page queues must be locked. 79449ca10d4SJayachandran C. */ 79549ca10d4SJayachandran C. vm_page_t 7960db2102aSMichael Zhilin vm_phys_alloc_freelist_pages(int domain, int freelist, int pool, int order) 79749ca10d4SJayachandran C. { 798ef435ae7SJeff Roberson struct vm_freelist *alt, *fl; 79911752d88SAlan Cox vm_page_t m; 8000db2102aSMichael Zhilin int oind, pind, flind; 80111752d88SAlan Cox 802ef435ae7SJeff Roberson KASSERT(domain >= 0 && domain < vm_ndomains, 803ef435ae7SJeff Roberson ("vm_phys_alloc_freelist_pages: domain %d is out of range", 804ef435ae7SJeff Roberson domain)); 8050db2102aSMichael Zhilin KASSERT(freelist < VM_NFREELIST, 806d866a563SAlan Cox ("vm_phys_alloc_freelist_pages: freelist %d is out of range", 8075be93778SAndrew Turner freelist)); 80811752d88SAlan Cox KASSERT(pool < VM_NFREEPOOL, 80949ca10d4SJayachandran C. ("vm_phys_alloc_freelist_pages: pool %d is out of range", pool)); 81011752d88SAlan Cox KASSERT(order < VM_NFREEORDER, 81149ca10d4SJayachandran C. ("vm_phys_alloc_freelist_pages: order %d is out of range", order)); 8126520495aSAdrian Chadd 8130db2102aSMichael Zhilin flind = vm_freelist_to_flind[freelist]; 8140db2102aSMichael Zhilin /* Check if freelist is present */ 8150db2102aSMichael Zhilin if (flind < 0) 8160db2102aSMichael Zhilin return (NULL); 8170db2102aSMichael Zhilin 818e2068d0bSJeff Roberson vm_domain_free_assert_locked(VM_DOMAIN(domain)); 8197e226537SAttilio Rao fl = &vm_phys_free_queues[domain][flind][pool][0]; 82011752d88SAlan Cox for (oind = order; oind < VM_NFREEORDER; oind++) { 82111752d88SAlan Cox m = TAILQ_FIRST(&fl[oind].pl); 82211752d88SAlan Cox if (m != NULL) { 8237e226537SAttilio Rao vm_freelist_rem(fl, m, oind); 824370a338aSAlan Cox /* The order [order, oind) queues are empty. */ 825370a338aSAlan Cox vm_phys_split_pages(m, oind, fl, order, 1); 82611752d88SAlan Cox return (m); 82711752d88SAlan Cox } 82811752d88SAlan Cox } 82911752d88SAlan Cox 83011752d88SAlan Cox /* 83111752d88SAlan Cox * The given pool was empty. Find the largest 83211752d88SAlan Cox * contiguous, power-of-two-sized set of pages in any 83311752d88SAlan Cox * pool. Transfer these pages to the given pool, and 83411752d88SAlan Cox * use them to satisfy the allocation. 83511752d88SAlan Cox */ 83611752d88SAlan Cox for (oind = VM_NFREEORDER - 1; oind >= order; oind--) { 83711752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 8387e226537SAttilio Rao alt = &vm_phys_free_queues[domain][flind][pind][0]; 83911752d88SAlan Cox m = TAILQ_FIRST(&alt[oind].pl); 84011752d88SAlan Cox if (m != NULL) { 8417e226537SAttilio Rao vm_freelist_rem(alt, m, oind); 84211752d88SAlan Cox vm_phys_set_pool(pool, m, oind); 843370a338aSAlan Cox /* The order [order, oind) queues are empty. */ 844370a338aSAlan Cox vm_phys_split_pages(m, oind, fl, order, 1); 84511752d88SAlan Cox return (m); 84611752d88SAlan Cox } 84711752d88SAlan Cox } 84811752d88SAlan Cox } 84911752d88SAlan Cox return (NULL); 85011752d88SAlan Cox } 85111752d88SAlan Cox 85211752d88SAlan Cox /* 85311752d88SAlan Cox * Find the vm_page corresponding to the given physical address. 85411752d88SAlan Cox */ 85511752d88SAlan Cox vm_page_t 85611752d88SAlan Cox vm_phys_paddr_to_vm_page(vm_paddr_t pa) 85711752d88SAlan Cox { 85811752d88SAlan Cox struct vm_phys_seg *seg; 85911752d88SAlan Cox int segind; 86011752d88SAlan Cox 86111752d88SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 86211752d88SAlan Cox seg = &vm_phys_segs[segind]; 86311752d88SAlan Cox if (pa >= seg->start && pa < seg->end) 86411752d88SAlan Cox return (&seg->first_page[atop(pa - seg->start)]); 86511752d88SAlan Cox } 866f06a3a36SAndrew Thompson return (NULL); 86711752d88SAlan Cox } 86811752d88SAlan Cox 869b6de32bdSKonstantin Belousov vm_page_t 870b6de32bdSKonstantin Belousov vm_phys_fictitious_to_vm_page(vm_paddr_t pa) 871b6de32bdSKonstantin Belousov { 87238d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg tmp, *seg; 873b6de32bdSKonstantin Belousov vm_page_t m; 874b6de32bdSKonstantin Belousov 875b6de32bdSKonstantin Belousov m = NULL; 87638d6b2dcSRoger Pau Monné tmp.start = pa; 87738d6b2dcSRoger Pau Monné tmp.end = 0; 87838d6b2dcSRoger Pau Monné 87938d6b2dcSRoger Pau Monné rw_rlock(&vm_phys_fictitious_reg_lock); 88038d6b2dcSRoger Pau Monné seg = RB_FIND(fict_tree, &vm_phys_fictitious_tree, &tmp); 88138d6b2dcSRoger Pau Monné rw_runlock(&vm_phys_fictitious_reg_lock); 88238d6b2dcSRoger Pau Monné if (seg == NULL) 88338d6b2dcSRoger Pau Monné return (NULL); 88438d6b2dcSRoger Pau Monné 885b6de32bdSKonstantin Belousov m = &seg->first_page[atop(pa - seg->start)]; 88638d6b2dcSRoger Pau Monné KASSERT((m->flags & PG_FICTITIOUS) != 0, ("%p not fictitious", m)); 88738d6b2dcSRoger Pau Monné 888b6de32bdSKonstantin Belousov return (m); 889b6de32bdSKonstantin Belousov } 890b6de32bdSKonstantin Belousov 8915ebe728dSRoger Pau Monné static inline void 8925ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(vm_page_t range, vm_paddr_t start, 8935ebe728dSRoger Pau Monné long page_count, vm_memattr_t memattr) 8945ebe728dSRoger Pau Monné { 8955ebe728dSRoger Pau Monné long i; 8965ebe728dSRoger Pau Monné 897f93f7cf1SMark Johnston bzero(range, page_count * sizeof(*range)); 8985ebe728dSRoger Pau Monné for (i = 0; i < page_count; i++) { 8995ebe728dSRoger Pau Monné vm_page_initfake(&range[i], start + PAGE_SIZE * i, memattr); 9005ebe728dSRoger Pau Monné range[i].oflags &= ~VPO_UNMANAGED; 9015ebe728dSRoger Pau Monné range[i].busy_lock = VPB_UNBUSIED; 9025ebe728dSRoger Pau Monné } 9035ebe728dSRoger Pau Monné } 9045ebe728dSRoger Pau Monné 905b6de32bdSKonstantin Belousov int 906b6de32bdSKonstantin Belousov vm_phys_fictitious_reg_range(vm_paddr_t start, vm_paddr_t end, 907b6de32bdSKonstantin Belousov vm_memattr_t memattr) 908b6de32bdSKonstantin Belousov { 909b6de32bdSKonstantin Belousov struct vm_phys_fictitious_seg *seg; 910b6de32bdSKonstantin Belousov vm_page_t fp; 9115ebe728dSRoger Pau Monné long page_count; 912b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 9135ebe728dSRoger Pau Monné long pi, pe; 9145ebe728dSRoger Pau Monné long dpage_count; 915b6de32bdSKonstantin Belousov #endif 916b6de32bdSKonstantin Belousov 9175ebe728dSRoger Pau Monné KASSERT(start < end, 9185ebe728dSRoger Pau Monné ("Start of segment isn't less than end (start: %jx end: %jx)", 9195ebe728dSRoger Pau Monné (uintmax_t)start, (uintmax_t)end)); 9205ebe728dSRoger Pau Monné 921b6de32bdSKonstantin Belousov page_count = (end - start) / PAGE_SIZE; 922b6de32bdSKonstantin Belousov 923b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 924b6de32bdSKonstantin Belousov pi = atop(start); 9255ebe728dSRoger Pau Monné pe = atop(end); 9265ebe728dSRoger Pau Monné if (pi >= first_page && (pi - first_page) < vm_page_array_size) { 927b6de32bdSKonstantin Belousov fp = &vm_page_array[pi - first_page]; 9285ebe728dSRoger Pau Monné if ((pe - first_page) > vm_page_array_size) { 9295ebe728dSRoger Pau Monné /* 9305ebe728dSRoger Pau Monné * We have a segment that starts inside 9315ebe728dSRoger Pau Monné * of vm_page_array, but ends outside of it. 9325ebe728dSRoger Pau Monné * 9335ebe728dSRoger Pau Monné * Use vm_page_array pages for those that are 9345ebe728dSRoger Pau Monné * inside of the vm_page_array range, and 9355ebe728dSRoger Pau Monné * allocate the remaining ones. 9365ebe728dSRoger Pau Monné */ 9375ebe728dSRoger Pau Monné dpage_count = vm_page_array_size - (pi - first_page); 9385ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, start, dpage_count, 9395ebe728dSRoger Pau Monné memattr); 9405ebe728dSRoger Pau Monné page_count -= dpage_count; 9415ebe728dSRoger Pau Monné start += ptoa(dpage_count); 9425ebe728dSRoger Pau Monné goto alloc; 9435ebe728dSRoger Pau Monné } 9445ebe728dSRoger Pau Monné /* 9455ebe728dSRoger Pau Monné * We can allocate the full range from vm_page_array, 9465ebe728dSRoger Pau Monné * so there's no need to register the range in the tree. 9475ebe728dSRoger Pau Monné */ 9485ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, start, page_count, memattr); 9495ebe728dSRoger Pau Monné return (0); 9505ebe728dSRoger Pau Monné } else if (pe > first_page && (pe - first_page) < vm_page_array_size) { 9515ebe728dSRoger Pau Monné /* 9525ebe728dSRoger Pau Monné * We have a segment that ends inside of vm_page_array, 9535ebe728dSRoger Pau Monné * but starts outside of it. 9545ebe728dSRoger Pau Monné */ 9555ebe728dSRoger Pau Monné fp = &vm_page_array[0]; 9565ebe728dSRoger Pau Monné dpage_count = pe - first_page; 9575ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, ptoa(first_page), dpage_count, 9585ebe728dSRoger Pau Monné memattr); 9595ebe728dSRoger Pau Monné end -= ptoa(dpage_count); 9605ebe728dSRoger Pau Monné page_count -= dpage_count; 9615ebe728dSRoger Pau Monné goto alloc; 9625ebe728dSRoger Pau Monné } else if (pi < first_page && pe > (first_page + vm_page_array_size)) { 9635ebe728dSRoger Pau Monné /* 9645ebe728dSRoger Pau Monné * Trying to register a fictitious range that expands before 9655ebe728dSRoger Pau Monné * and after vm_page_array. 9665ebe728dSRoger Pau Monné */ 9675ebe728dSRoger Pau Monné return (EINVAL); 9685ebe728dSRoger Pau Monné } else { 9695ebe728dSRoger Pau Monné alloc: 970b6de32bdSKonstantin Belousov #endif 971b6de32bdSKonstantin Belousov fp = malloc(page_count * sizeof(struct vm_page), M_FICT_PAGES, 972f93f7cf1SMark Johnston M_WAITOK); 9735ebe728dSRoger Pau Monné #ifdef VM_PHYSSEG_DENSE 974b6de32bdSKonstantin Belousov } 9755ebe728dSRoger Pau Monné #endif 9765ebe728dSRoger Pau Monné vm_phys_fictitious_init_range(fp, start, page_count, memattr); 97738d6b2dcSRoger Pau Monné 97838d6b2dcSRoger Pau Monné seg = malloc(sizeof(*seg), M_FICT_PAGES, M_WAITOK | M_ZERO); 979b6de32bdSKonstantin Belousov seg->start = start; 980b6de32bdSKonstantin Belousov seg->end = end; 981b6de32bdSKonstantin Belousov seg->first_page = fp; 98238d6b2dcSRoger Pau Monné 98338d6b2dcSRoger Pau Monné rw_wlock(&vm_phys_fictitious_reg_lock); 98438d6b2dcSRoger Pau Monné RB_INSERT(fict_tree, &vm_phys_fictitious_tree, seg); 98538d6b2dcSRoger Pau Monné rw_wunlock(&vm_phys_fictitious_reg_lock); 98638d6b2dcSRoger Pau Monné 987b6de32bdSKonstantin Belousov return (0); 988b6de32bdSKonstantin Belousov } 989b6de32bdSKonstantin Belousov 990b6de32bdSKonstantin Belousov void 991b6de32bdSKonstantin Belousov vm_phys_fictitious_unreg_range(vm_paddr_t start, vm_paddr_t end) 992b6de32bdSKonstantin Belousov { 99338d6b2dcSRoger Pau Monné struct vm_phys_fictitious_seg *seg, tmp; 994b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 9955ebe728dSRoger Pau Monné long pi, pe; 996b6de32bdSKonstantin Belousov #endif 997b6de32bdSKonstantin Belousov 9985ebe728dSRoger Pau Monné KASSERT(start < end, 9995ebe728dSRoger Pau Monné ("Start of segment isn't less than end (start: %jx end: %jx)", 10005ebe728dSRoger Pau Monné (uintmax_t)start, (uintmax_t)end)); 10015ebe728dSRoger Pau Monné 1002b6de32bdSKonstantin Belousov #ifdef VM_PHYSSEG_DENSE 1003b6de32bdSKonstantin Belousov pi = atop(start); 10045ebe728dSRoger Pau Monné pe = atop(end); 10055ebe728dSRoger Pau Monné if (pi >= first_page && (pi - first_page) < vm_page_array_size) { 10065ebe728dSRoger Pau Monné if ((pe - first_page) <= vm_page_array_size) { 10075ebe728dSRoger Pau Monné /* 10085ebe728dSRoger Pau Monné * This segment was allocated using vm_page_array 10095ebe728dSRoger Pau Monné * only, there's nothing to do since those pages 10105ebe728dSRoger Pau Monné * were never added to the tree. 10115ebe728dSRoger Pau Monné */ 10125ebe728dSRoger Pau Monné return; 10135ebe728dSRoger Pau Monné } 10145ebe728dSRoger Pau Monné /* 10155ebe728dSRoger Pau Monné * We have a segment that starts inside 10165ebe728dSRoger Pau Monné * of vm_page_array, but ends outside of it. 10175ebe728dSRoger Pau Monné * 10185ebe728dSRoger Pau Monné * Calculate how many pages were added to the 10195ebe728dSRoger Pau Monné * tree and free them. 10205ebe728dSRoger Pau Monné */ 10215ebe728dSRoger Pau Monné start = ptoa(first_page + vm_page_array_size); 10225ebe728dSRoger Pau Monné } else if (pe > first_page && (pe - first_page) < vm_page_array_size) { 10235ebe728dSRoger Pau Monné /* 10245ebe728dSRoger Pau Monné * We have a segment that ends inside of vm_page_array, 10255ebe728dSRoger Pau Monné * but starts outside of it. 10265ebe728dSRoger Pau Monné */ 10275ebe728dSRoger Pau Monné end = ptoa(first_page); 10285ebe728dSRoger Pau Monné } else if (pi < first_page && pe > (first_page + vm_page_array_size)) { 10295ebe728dSRoger Pau Monné /* Since it's not possible to register such a range, panic. */ 10305ebe728dSRoger Pau Monné panic( 10315ebe728dSRoger Pau Monné "Unregistering not registered fictitious range [%#jx:%#jx]", 10325ebe728dSRoger Pau Monné (uintmax_t)start, (uintmax_t)end); 10335ebe728dSRoger Pau Monné } 1034b6de32bdSKonstantin Belousov #endif 103538d6b2dcSRoger Pau Monné tmp.start = start; 103638d6b2dcSRoger Pau Monné tmp.end = 0; 1037b6de32bdSKonstantin Belousov 103838d6b2dcSRoger Pau Monné rw_wlock(&vm_phys_fictitious_reg_lock); 103938d6b2dcSRoger Pau Monné seg = RB_FIND(fict_tree, &vm_phys_fictitious_tree, &tmp); 104038d6b2dcSRoger Pau Monné if (seg->start != start || seg->end != end) { 104138d6b2dcSRoger Pau Monné rw_wunlock(&vm_phys_fictitious_reg_lock); 104238d6b2dcSRoger Pau Monné panic( 104338d6b2dcSRoger Pau Monné "Unregistering not registered fictitious range [%#jx:%#jx]", 104438d6b2dcSRoger Pau Monné (uintmax_t)start, (uintmax_t)end); 104538d6b2dcSRoger Pau Monné } 104638d6b2dcSRoger Pau Monné RB_REMOVE(fict_tree, &vm_phys_fictitious_tree, seg); 104738d6b2dcSRoger Pau Monné rw_wunlock(&vm_phys_fictitious_reg_lock); 104838d6b2dcSRoger Pau Monné free(seg->first_page, M_FICT_PAGES); 104938d6b2dcSRoger Pau Monné free(seg, M_FICT_PAGES); 1050b6de32bdSKonstantin Belousov } 1051b6de32bdSKonstantin Belousov 105211752d88SAlan Cox /* 105311752d88SAlan Cox * Free a contiguous, power of two-sized set of physical pages. 10548941dc44SAlan Cox * 10558941dc44SAlan Cox * The free page queues must be locked. 105611752d88SAlan Cox */ 105711752d88SAlan Cox void 105811752d88SAlan Cox vm_phys_free_pages(vm_page_t m, int order) 105911752d88SAlan Cox { 106011752d88SAlan Cox struct vm_freelist *fl; 106111752d88SAlan Cox struct vm_phys_seg *seg; 10625c1f2cc4SAlan Cox vm_paddr_t pa; 106311752d88SAlan Cox vm_page_t m_buddy; 106411752d88SAlan Cox 106511752d88SAlan Cox KASSERT(m->order == VM_NFREEORDER, 10668941dc44SAlan Cox ("vm_phys_free_pages: page %p has unexpected order %d", 106711752d88SAlan Cox m, m->order)); 106811752d88SAlan Cox KASSERT(m->pool < VM_NFREEPOOL, 10698941dc44SAlan Cox ("vm_phys_free_pages: page %p has unexpected pool %d", 107011752d88SAlan Cox m, m->pool)); 107111752d88SAlan Cox KASSERT(order < VM_NFREEORDER, 10728941dc44SAlan Cox ("vm_phys_free_pages: order %d is out of range", order)); 107311752d88SAlan Cox seg = &vm_phys_segs[m->segind]; 1074e2068d0bSJeff Roberson vm_domain_free_assert_locked(VM_DOMAIN(seg->domain)); 10755c1f2cc4SAlan Cox if (order < VM_NFREEORDER - 1) { 10765c1f2cc4SAlan Cox pa = VM_PAGE_TO_PHYS(m); 10775c1f2cc4SAlan Cox do { 10785c1f2cc4SAlan Cox pa ^= ((vm_paddr_t)1 << (PAGE_SHIFT + order)); 10795c1f2cc4SAlan Cox if (pa < seg->start || pa >= seg->end) 108011752d88SAlan Cox break; 10815c1f2cc4SAlan Cox m_buddy = &seg->first_page[atop(pa - seg->start)]; 108211752d88SAlan Cox if (m_buddy->order != order) 108311752d88SAlan Cox break; 108411752d88SAlan Cox fl = (*seg->free_queues)[m_buddy->pool]; 10857e226537SAttilio Rao vm_freelist_rem(fl, m_buddy, order); 108611752d88SAlan Cox if (m_buddy->pool != m->pool) 108711752d88SAlan Cox vm_phys_set_pool(m->pool, m_buddy, order); 108811752d88SAlan Cox order++; 10895c1f2cc4SAlan Cox pa &= ~(((vm_paddr_t)1 << (PAGE_SHIFT + order)) - 1); 109011752d88SAlan Cox m = &seg->first_page[atop(pa - seg->start)]; 10915c1f2cc4SAlan Cox } while (order < VM_NFREEORDER - 1); 109211752d88SAlan Cox } 109311752d88SAlan Cox fl = (*seg->free_queues)[m->pool]; 10947e226537SAttilio Rao vm_freelist_add(fl, m, order, 1); 109511752d88SAlan Cox } 109611752d88SAlan Cox 109711752d88SAlan Cox /* 1098*b8590daeSDoug Moore * Return the largest possible order of a set of pages starting at m. 10995c1f2cc4SAlan Cox */ 1100*b8590daeSDoug Moore static int 1101*b8590daeSDoug Moore max_order(vm_page_t m) 11025c1f2cc4SAlan Cox { 11035c1f2cc4SAlan Cox 11045c1f2cc4SAlan Cox /* 11055c1f2cc4SAlan Cox * Unsigned "min" is used here so that "order" is assigned 11065c1f2cc4SAlan Cox * "VM_NFREEORDER - 1" when "m"'s physical address is zero 11075c1f2cc4SAlan Cox * or the low-order bits of its physical address are zero 11085c1f2cc4SAlan Cox * because the size of a physical address exceeds the size of 11095c1f2cc4SAlan Cox * a long. 11105c1f2cc4SAlan Cox */ 1111*b8590daeSDoug Moore return (min(ffsl(VM_PAGE_TO_PHYS(m) >> PAGE_SHIFT) - 1, 1112*b8590daeSDoug Moore VM_NFREEORDER - 1)); 11135c1f2cc4SAlan Cox } 1114*b8590daeSDoug Moore 1115*b8590daeSDoug Moore /* 1116*b8590daeSDoug Moore * Free a contiguous, arbitrarily sized set of physical pages, without 1117*b8590daeSDoug Moore * merging across set boundaries. 1118*b8590daeSDoug Moore * 1119*b8590daeSDoug Moore * The free page queues must be locked. 1120*b8590daeSDoug Moore */ 1121*b8590daeSDoug Moore void 1122*b8590daeSDoug Moore vm_phys_enqueue_contig(vm_page_t m, u_long npages) 1123*b8590daeSDoug Moore { 1124*b8590daeSDoug Moore struct vm_freelist *fl; 1125*b8590daeSDoug Moore struct vm_phys_seg *seg; 1126*b8590daeSDoug Moore vm_page_t m_end; 1127*b8590daeSDoug Moore int order; 1128*b8590daeSDoug Moore 1129*b8590daeSDoug Moore /* 1130*b8590daeSDoug Moore * Avoid unnecessary coalescing by freeing the pages in the largest 1131*b8590daeSDoug Moore * possible power-of-two-sized subsets. 1132*b8590daeSDoug Moore */ 1133*b8590daeSDoug Moore vm_domain_free_assert_locked(vm_pagequeue_domain(m)); 1134*b8590daeSDoug Moore seg = &vm_phys_segs[m->segind]; 1135*b8590daeSDoug Moore fl = (*seg->free_queues)[m->pool]; 1136*b8590daeSDoug Moore m_end = m + npages; 1137*b8590daeSDoug Moore /* Free blocks of increasing size. */ 1138*b8590daeSDoug Moore while ((order = max_order(m)) < VM_NFREEORDER - 1 && 1139*b8590daeSDoug Moore m + (1 << order) <= m_end) { 1140*b8590daeSDoug Moore KASSERT(seg == &vm_phys_segs[m->segind], 1141*b8590daeSDoug Moore ("%s: page range [%p,%p) spans multiple segments", 1142*b8590daeSDoug Moore __func__, m_end - npages, m)); 1143*b8590daeSDoug Moore vm_freelist_add(fl, m, order, 1); 1144*b8590daeSDoug Moore m += 1 << order; 11455c1f2cc4SAlan Cox } 1146*b8590daeSDoug Moore /* Free blocks of maximum size. */ 1147*b8590daeSDoug Moore while (m + (1 << order) <= m_end) { 1148*b8590daeSDoug Moore KASSERT(seg == &vm_phys_segs[m->segind], 1149*b8590daeSDoug Moore ("%s: page range [%p,%p) spans multiple segments", 1150*b8590daeSDoug Moore __func__, m_end - npages, m)); 1151*b8590daeSDoug Moore vm_freelist_add(fl, m, order, 1); 1152*b8590daeSDoug Moore m += 1 << order; 1153*b8590daeSDoug Moore } 1154*b8590daeSDoug Moore /* Free blocks of diminishing size. */ 1155*b8590daeSDoug Moore while (m < m_end) { 1156*b8590daeSDoug Moore KASSERT(seg == &vm_phys_segs[m->segind], 1157*b8590daeSDoug Moore ("%s: page range [%p,%p) spans multiple segments", 1158*b8590daeSDoug Moore __func__, m_end - npages, m)); 1159*b8590daeSDoug Moore order = flsl(m_end - m) - 1; 1160*b8590daeSDoug Moore vm_freelist_add(fl, m, order, 1); 1161*b8590daeSDoug Moore m += 1 << order; 1162*b8590daeSDoug Moore } 1163*b8590daeSDoug Moore } 1164*b8590daeSDoug Moore 1165*b8590daeSDoug Moore /* 1166*b8590daeSDoug Moore * Free a contiguous, arbitrarily sized set of physical pages. 1167*b8590daeSDoug Moore * 1168*b8590daeSDoug Moore * The free page queues must be locked. 1169*b8590daeSDoug Moore */ 1170*b8590daeSDoug Moore void 1171*b8590daeSDoug Moore vm_phys_free_contig(vm_page_t m, u_long npages) 1172*b8590daeSDoug Moore { 1173*b8590daeSDoug Moore int order_start, order_end; 1174*b8590daeSDoug Moore vm_page_t m_start, m_end; 1175*b8590daeSDoug Moore 1176*b8590daeSDoug Moore vm_domain_free_assert_locked(vm_pagequeue_domain(m)); 1177*b8590daeSDoug Moore 1178*b8590daeSDoug Moore m_start = m; 1179*b8590daeSDoug Moore order_start = max_order(m_start); 1180*b8590daeSDoug Moore if (order_start < VM_NFREEORDER - 1) 1181*b8590daeSDoug Moore m_start += 1 << order_start; 1182*b8590daeSDoug Moore m_end = m + npages; 1183*b8590daeSDoug Moore order_end = max_order(m_end); 1184*b8590daeSDoug Moore if (order_end < VM_NFREEORDER - 1) 1185*b8590daeSDoug Moore m_end -= 1 << order_end; 1186*b8590daeSDoug Moore /* 1187*b8590daeSDoug Moore * Avoid unnecessary coalescing by freeing the pages at the start and 1188*b8590daeSDoug Moore * end of the range last. 1189*b8590daeSDoug Moore */ 1190*b8590daeSDoug Moore if (m_start < m_end) 1191*b8590daeSDoug Moore vm_phys_enqueue_contig(m_start, m_end - m_start); 1192*b8590daeSDoug Moore if (order_start < VM_NFREEORDER - 1) 1193*b8590daeSDoug Moore vm_phys_free_pages(m, order_start); 1194*b8590daeSDoug Moore if (order_end < VM_NFREEORDER - 1) 1195*b8590daeSDoug Moore vm_phys_free_pages(m_end, order_end); 11965c1f2cc4SAlan Cox } 11975c1f2cc4SAlan Cox 11985c1f2cc4SAlan Cox /* 1199c869e672SAlan Cox * Scan physical memory between the specified addresses "low" and "high" for a 1200c869e672SAlan Cox * run of contiguous physical pages that satisfy the specified conditions, and 1201c869e672SAlan Cox * return the lowest page in the run. The specified "alignment" determines 1202c869e672SAlan Cox * the alignment of the lowest physical page in the run. If the specified 1203c869e672SAlan Cox * "boundary" is non-zero, then the run of physical pages cannot span a 1204c869e672SAlan Cox * physical address that is a multiple of "boundary". 1205c869e672SAlan Cox * 1206c869e672SAlan Cox * "npages" must be greater than zero. Both "alignment" and "boundary" must 1207c869e672SAlan Cox * be a power of two. 1208c869e672SAlan Cox */ 1209c869e672SAlan Cox vm_page_t 12103f289c3fSJeff Roberson vm_phys_scan_contig(int domain, u_long npages, vm_paddr_t low, vm_paddr_t high, 1211c869e672SAlan Cox u_long alignment, vm_paddr_t boundary, int options) 1212c869e672SAlan Cox { 1213c869e672SAlan Cox vm_paddr_t pa_end; 1214c869e672SAlan Cox vm_page_t m_end, m_run, m_start; 1215c869e672SAlan Cox struct vm_phys_seg *seg; 1216c869e672SAlan Cox int segind; 1217c869e672SAlan Cox 1218c869e672SAlan Cox KASSERT(npages > 0, ("npages is 0")); 1219c869e672SAlan Cox KASSERT(powerof2(alignment), ("alignment is not a power of 2")); 1220c869e672SAlan Cox KASSERT(powerof2(boundary), ("boundary is not a power of 2")); 1221c869e672SAlan Cox if (low >= high) 1222c869e672SAlan Cox return (NULL); 1223c869e672SAlan Cox for (segind = 0; segind < vm_phys_nsegs; segind++) { 1224c869e672SAlan Cox seg = &vm_phys_segs[segind]; 12253f289c3fSJeff Roberson if (seg->domain != domain) 12263f289c3fSJeff Roberson continue; 1227c869e672SAlan Cox if (seg->start >= high) 1228c869e672SAlan Cox break; 1229c869e672SAlan Cox if (low >= seg->end) 1230c869e672SAlan Cox continue; 1231c869e672SAlan Cox if (low <= seg->start) 1232c869e672SAlan Cox m_start = seg->first_page; 1233c869e672SAlan Cox else 1234c869e672SAlan Cox m_start = &seg->first_page[atop(low - seg->start)]; 1235c869e672SAlan Cox if (high < seg->end) 1236c869e672SAlan Cox pa_end = high; 1237c869e672SAlan Cox else 1238c869e672SAlan Cox pa_end = seg->end; 1239c869e672SAlan Cox if (pa_end - VM_PAGE_TO_PHYS(m_start) < ptoa(npages)) 1240c869e672SAlan Cox continue; 1241c869e672SAlan Cox m_end = &seg->first_page[atop(pa_end - seg->start)]; 1242c869e672SAlan Cox m_run = vm_page_scan_contig(npages, m_start, m_end, 1243c869e672SAlan Cox alignment, boundary, options); 1244c869e672SAlan Cox if (m_run != NULL) 1245c869e672SAlan Cox return (m_run); 1246c869e672SAlan Cox } 1247c869e672SAlan Cox return (NULL); 1248c869e672SAlan Cox } 1249c869e672SAlan Cox 1250c869e672SAlan Cox /* 125111752d88SAlan Cox * Set the pool for a contiguous, power of two-sized set of physical pages. 125211752d88SAlan Cox */ 12537bfda801SAlan Cox void 125411752d88SAlan Cox vm_phys_set_pool(int pool, vm_page_t m, int order) 125511752d88SAlan Cox { 125611752d88SAlan Cox vm_page_t m_tmp; 125711752d88SAlan Cox 125811752d88SAlan Cox for (m_tmp = m; m_tmp < &m[1 << order]; m_tmp++) 125911752d88SAlan Cox m_tmp->pool = pool; 126011752d88SAlan Cox } 126111752d88SAlan Cox 126211752d88SAlan Cox /* 12639742373aSAlan Cox * Search for the given physical page "m" in the free lists. If the search 12649742373aSAlan Cox * succeeds, remove "m" from the free lists and return TRUE. Otherwise, return 12659742373aSAlan Cox * FALSE, indicating that "m" is not in the free lists. 12667bfda801SAlan Cox * 12677bfda801SAlan Cox * The free page queues must be locked. 12687bfda801SAlan Cox */ 1269e35395ceSAlan Cox boolean_t 12707bfda801SAlan Cox vm_phys_unfree_page(vm_page_t m) 12717bfda801SAlan Cox { 12727bfda801SAlan Cox struct vm_freelist *fl; 12737bfda801SAlan Cox struct vm_phys_seg *seg; 12747bfda801SAlan Cox vm_paddr_t pa, pa_half; 12757bfda801SAlan Cox vm_page_t m_set, m_tmp; 12767bfda801SAlan Cox int order; 12777bfda801SAlan Cox 12787bfda801SAlan Cox /* 12797bfda801SAlan Cox * First, find the contiguous, power of two-sized set of free 12807bfda801SAlan Cox * physical pages containing the given physical page "m" and 12817bfda801SAlan Cox * assign it to "m_set". 12827bfda801SAlan Cox */ 12837bfda801SAlan Cox seg = &vm_phys_segs[m->segind]; 1284e2068d0bSJeff Roberson vm_domain_free_assert_locked(VM_DOMAIN(seg->domain)); 12857bfda801SAlan Cox for (m_set = m, order = 0; m_set->order == VM_NFREEORDER && 1286bc8794a1SAlan Cox order < VM_NFREEORDER - 1; ) { 12877bfda801SAlan Cox order++; 12887bfda801SAlan Cox pa = m->phys_addr & (~(vm_paddr_t)0 << (PAGE_SHIFT + order)); 12892fbced65SAlan Cox if (pa >= seg->start) 12907bfda801SAlan Cox m_set = &seg->first_page[atop(pa - seg->start)]; 1291e35395ceSAlan Cox else 1292e35395ceSAlan Cox return (FALSE); 12937bfda801SAlan Cox } 1294e35395ceSAlan Cox if (m_set->order < order) 1295e35395ceSAlan Cox return (FALSE); 1296e35395ceSAlan Cox if (m_set->order == VM_NFREEORDER) 1297e35395ceSAlan Cox return (FALSE); 12987bfda801SAlan Cox KASSERT(m_set->order < VM_NFREEORDER, 12997bfda801SAlan Cox ("vm_phys_unfree_page: page %p has unexpected order %d", 13007bfda801SAlan Cox m_set, m_set->order)); 13017bfda801SAlan Cox 13027bfda801SAlan Cox /* 13037bfda801SAlan Cox * Next, remove "m_set" from the free lists. Finally, extract 13047bfda801SAlan Cox * "m" from "m_set" using an iterative algorithm: While "m_set" 13057bfda801SAlan Cox * is larger than a page, shrink "m_set" by returning the half 13067bfda801SAlan Cox * of "m_set" that does not contain "m" to the free lists. 13077bfda801SAlan Cox */ 13087bfda801SAlan Cox fl = (*seg->free_queues)[m_set->pool]; 13097bfda801SAlan Cox order = m_set->order; 13107e226537SAttilio Rao vm_freelist_rem(fl, m_set, order); 13117bfda801SAlan Cox while (order > 0) { 13127bfda801SAlan Cox order--; 13137bfda801SAlan Cox pa_half = m_set->phys_addr ^ (1 << (PAGE_SHIFT + order)); 13147bfda801SAlan Cox if (m->phys_addr < pa_half) 13157bfda801SAlan Cox m_tmp = &seg->first_page[atop(pa_half - seg->start)]; 13167bfda801SAlan Cox else { 13177bfda801SAlan Cox m_tmp = m_set; 13187bfda801SAlan Cox m_set = &seg->first_page[atop(pa_half - seg->start)]; 13197bfda801SAlan Cox } 13207e226537SAttilio Rao vm_freelist_add(fl, m_tmp, order, 0); 13217bfda801SAlan Cox } 13227bfda801SAlan Cox KASSERT(m_set == m, ("vm_phys_unfree_page: fatal inconsistency")); 1323e35395ceSAlan Cox return (TRUE); 13247bfda801SAlan Cox } 13257bfda801SAlan Cox 13267bfda801SAlan Cox /* 13272f9f48d6SAlan Cox * Allocate a contiguous set of physical pages of the given size 13282f9f48d6SAlan Cox * "npages" from the free lists. All of the physical pages must be at 13292f9f48d6SAlan Cox * or above the given physical address "low" and below the given 13302f9f48d6SAlan Cox * physical address "high". The given value "alignment" determines the 13312f9f48d6SAlan Cox * alignment of the first physical page in the set. If the given value 13322f9f48d6SAlan Cox * "boundary" is non-zero, then the set of physical pages cannot cross 13332f9f48d6SAlan Cox * any physical address boundary that is a multiple of that value. Both 133411752d88SAlan Cox * "alignment" and "boundary" must be a power of two. 133511752d88SAlan Cox */ 133611752d88SAlan Cox vm_page_t 1337ef435ae7SJeff Roberson vm_phys_alloc_contig(int domain, u_long npages, vm_paddr_t low, vm_paddr_t high, 13385c1f2cc4SAlan Cox u_long alignment, vm_paddr_t boundary) 133911752d88SAlan Cox { 1340c869e672SAlan Cox vm_paddr_t pa_end, pa_start; 1341c869e672SAlan Cox vm_page_t m_run; 1342c869e672SAlan Cox struct vm_phys_seg *seg; 1343ef435ae7SJeff Roberson int segind; 134411752d88SAlan Cox 1345c869e672SAlan Cox KASSERT(npages > 0, ("npages is 0")); 1346c869e672SAlan Cox KASSERT(powerof2(alignment), ("alignment is not a power of 2")); 1347c869e672SAlan Cox KASSERT(powerof2(boundary), ("boundary is not a power of 2")); 1348e2068d0bSJeff Roberson vm_domain_free_assert_locked(VM_DOMAIN(domain)); 1349c869e672SAlan Cox if (low >= high) 1350c869e672SAlan Cox return (NULL); 1351c869e672SAlan Cox m_run = NULL; 1352477bffbeSAlan Cox for (segind = vm_phys_nsegs - 1; segind >= 0; segind--) { 1353c869e672SAlan Cox seg = &vm_phys_segs[segind]; 1354477bffbeSAlan Cox if (seg->start >= high || seg->domain != domain) 135511752d88SAlan Cox continue; 1356477bffbeSAlan Cox if (low >= seg->end) 1357477bffbeSAlan Cox break; 1358c869e672SAlan Cox if (low <= seg->start) 1359c869e672SAlan Cox pa_start = seg->start; 1360c869e672SAlan Cox else 1361c869e672SAlan Cox pa_start = low; 1362c869e672SAlan Cox if (high < seg->end) 1363c869e672SAlan Cox pa_end = high; 1364c869e672SAlan Cox else 1365c869e672SAlan Cox pa_end = seg->end; 1366c869e672SAlan Cox if (pa_end - pa_start < ptoa(npages)) 1367c869e672SAlan Cox continue; 1368c869e672SAlan Cox m_run = vm_phys_alloc_seg_contig(seg, npages, low, high, 1369c869e672SAlan Cox alignment, boundary); 1370c869e672SAlan Cox if (m_run != NULL) 1371c869e672SAlan Cox break; 1372c869e672SAlan Cox } 1373c869e672SAlan Cox return (m_run); 1374c869e672SAlan Cox } 137511752d88SAlan Cox 137611752d88SAlan Cox /* 1377c869e672SAlan Cox * Allocate a run of contiguous physical pages from the free list for the 1378c869e672SAlan Cox * specified segment. 1379c869e672SAlan Cox */ 1380c869e672SAlan Cox static vm_page_t 1381c869e672SAlan Cox vm_phys_alloc_seg_contig(struct vm_phys_seg *seg, u_long npages, 1382c869e672SAlan Cox vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary) 1383c869e672SAlan Cox { 1384c869e672SAlan Cox struct vm_freelist *fl; 1385c869e672SAlan Cox vm_paddr_t pa, pa_end, size; 1386c869e672SAlan Cox vm_page_t m, m_ret; 1387c869e672SAlan Cox u_long npages_end; 1388c869e672SAlan Cox int oind, order, pind; 1389c869e672SAlan Cox 1390c869e672SAlan Cox KASSERT(npages > 0, ("npages is 0")); 1391c869e672SAlan Cox KASSERT(powerof2(alignment), ("alignment is not a power of 2")); 1392c869e672SAlan Cox KASSERT(powerof2(boundary), ("boundary is not a power of 2")); 1393e2068d0bSJeff Roberson vm_domain_free_assert_locked(VM_DOMAIN(seg->domain)); 1394c869e672SAlan Cox /* Compute the queue that is the best fit for npages. */ 13959161b4deSAlan Cox order = flsl(npages - 1); 1396c869e672SAlan Cox /* Search for a run satisfying the specified conditions. */ 1397c869e672SAlan Cox size = npages << PAGE_SHIFT; 1398c869e672SAlan Cox for (oind = min(order, VM_NFREEORDER - 1); oind < VM_NFREEORDER; 1399c869e672SAlan Cox oind++) { 1400c869e672SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 1401c869e672SAlan Cox fl = (*seg->free_queues)[pind]; 14025cd29d0fSMark Johnston TAILQ_FOREACH(m_ret, &fl[oind].pl, listq) { 1403c869e672SAlan Cox /* 140411752d88SAlan Cox * Is the size of this allocation request 140511752d88SAlan Cox * larger than the largest block size? 140611752d88SAlan Cox */ 140711752d88SAlan Cox if (order >= VM_NFREEORDER) { 140811752d88SAlan Cox /* 1409c869e672SAlan Cox * Determine if a sufficient number of 1410c869e672SAlan Cox * subsequent blocks to satisfy the 1411c869e672SAlan Cox * allocation request are free. 141211752d88SAlan Cox */ 141311752d88SAlan Cox pa = VM_PAGE_TO_PHYS(m_ret); 1414c869e672SAlan Cox pa_end = pa + size; 141579e9552eSKonstantin Belousov if (pa_end < pa) 141679e9552eSKonstantin Belousov continue; 141711752d88SAlan Cox for (;;) { 1418c869e672SAlan Cox pa += 1 << (PAGE_SHIFT + 1419c869e672SAlan Cox VM_NFREEORDER - 1); 1420c869e672SAlan Cox if (pa >= pa_end || 1421c869e672SAlan Cox pa < seg->start || 142211752d88SAlan Cox pa >= seg->end) 142311752d88SAlan Cox break; 1424c869e672SAlan Cox m = &seg->first_page[atop(pa - 1425c869e672SAlan Cox seg->start)]; 1426c869e672SAlan Cox if (m->order != VM_NFREEORDER - 1427c869e672SAlan Cox 1) 142811752d88SAlan Cox break; 142911752d88SAlan Cox } 1430c869e672SAlan Cox /* If not, go to the next block. */ 1431c869e672SAlan Cox if (pa < pa_end) 143211752d88SAlan Cox continue; 143311752d88SAlan Cox } 143411752d88SAlan Cox 143511752d88SAlan Cox /* 1436c869e672SAlan Cox * Determine if the blocks are within the 1437c869e672SAlan Cox * given range, satisfy the given alignment, 1438c869e672SAlan Cox * and do not cross the given boundary. 143911752d88SAlan Cox */ 144011752d88SAlan Cox pa = VM_PAGE_TO_PHYS(m_ret); 1441c869e672SAlan Cox pa_end = pa + size; 1442d9c9c81cSPedro F. Giffuni if (pa >= low && pa_end <= high && 1443d9c9c81cSPedro F. Giffuni (pa & (alignment - 1)) == 0 && 1444d9c9c81cSPedro F. Giffuni rounddown2(pa ^ (pa_end - 1), boundary) == 0) 144511752d88SAlan Cox goto done; 144611752d88SAlan Cox } 144711752d88SAlan Cox } 144811752d88SAlan Cox } 144911752d88SAlan Cox return (NULL); 145011752d88SAlan Cox done: 145111752d88SAlan Cox for (m = m_ret; m < &m_ret[npages]; m = &m[1 << oind]) { 145211752d88SAlan Cox fl = (*seg->free_queues)[m->pool]; 14539161b4deSAlan Cox vm_freelist_rem(fl, m, oind); 14549161b4deSAlan Cox if (m->pool != VM_FREEPOOL_DEFAULT) 14559161b4deSAlan Cox vm_phys_set_pool(VM_FREEPOOL_DEFAULT, m, oind); 145611752d88SAlan Cox } 14575c1f2cc4SAlan Cox /* Return excess pages to the free lists. */ 14589161b4deSAlan Cox npages_end = roundup2(npages, 1 << oind); 14597493904eSAlan Cox if (npages < npages_end) { 14607493904eSAlan Cox fl = (*seg->free_queues)[VM_FREEPOOL_DEFAULT]; 14617493904eSAlan Cox vm_phys_enq_range(&m_ret[npages], npages_end - npages, fl, 0); 14627493904eSAlan Cox } 146311752d88SAlan Cox return (m_ret); 146411752d88SAlan Cox } 146511752d88SAlan Cox 146611752d88SAlan Cox #ifdef DDB 146711752d88SAlan Cox /* 146811752d88SAlan Cox * Show the number of physical pages in each of the free lists. 146911752d88SAlan Cox */ 147011752d88SAlan Cox DB_SHOW_COMMAND(freepages, db_show_freepages) 147111752d88SAlan Cox { 147211752d88SAlan Cox struct vm_freelist *fl; 14737e226537SAttilio Rao int flind, oind, pind, dom; 147411752d88SAlan Cox 14757e226537SAttilio Rao for (dom = 0; dom < vm_ndomains; dom++) { 14767e226537SAttilio Rao db_printf("DOMAIN: %d\n", dom); 147711752d88SAlan Cox for (flind = 0; flind < vm_nfreelists; flind++) { 147811752d88SAlan Cox db_printf("FREE LIST %d:\n" 147911752d88SAlan Cox "\n ORDER (SIZE) | NUMBER" 148011752d88SAlan Cox "\n ", flind); 148111752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 148211752d88SAlan Cox db_printf(" | POOL %d", pind); 148311752d88SAlan Cox db_printf("\n-- "); 148411752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) 148511752d88SAlan Cox db_printf("-- -- "); 148611752d88SAlan Cox db_printf("--\n"); 148711752d88SAlan Cox for (oind = VM_NFREEORDER - 1; oind >= 0; oind--) { 148811752d88SAlan Cox db_printf(" %2.2d (%6.6dK)", oind, 148911752d88SAlan Cox 1 << (PAGE_SHIFT - 10 + oind)); 149011752d88SAlan Cox for (pind = 0; pind < VM_NFREEPOOL; pind++) { 14917e226537SAttilio Rao fl = vm_phys_free_queues[dom][flind][pind]; 149211752d88SAlan Cox db_printf(" | %6.6d", fl[oind].lcnt); 149311752d88SAlan Cox } 149411752d88SAlan Cox db_printf("\n"); 149511752d88SAlan Cox } 149611752d88SAlan Cox db_printf("\n"); 149711752d88SAlan Cox } 14987e226537SAttilio Rao db_printf("\n"); 14997e226537SAttilio Rao } 150011752d88SAlan Cox } 150111752d88SAlan Cox #endif 1502