1f8a47341SAlan Cox /*- 2f8a47341SAlan Cox * Copyright (c) 2002-2006 Rice University 344aab2c3SAlan Cox * Copyright (c) 2007-2008 Alan L. Cox <alc@cs.rice.edu> 4f8a47341SAlan Cox * All rights reserved. 5f8a47341SAlan Cox * 6f8a47341SAlan Cox * This software was developed for the FreeBSD Project by Alan L. Cox, 7f8a47341SAlan Cox * Olivier Crameri, Peter Druschel, Sitaram Iyer, and Juan Navarro. 8f8a47341SAlan Cox * 9f8a47341SAlan Cox * Redistribution and use in source and binary forms, with or without 10f8a47341SAlan Cox * modification, are permitted provided that the following conditions 11f8a47341SAlan Cox * are met: 12f8a47341SAlan Cox * 1. Redistributions of source code must retain the above copyright 13f8a47341SAlan Cox * notice, this list of conditions and the following disclaimer. 14f8a47341SAlan Cox * 2. Redistributions in binary form must reproduce the above copyright 15f8a47341SAlan Cox * notice, this list of conditions and the following disclaimer in the 16f8a47341SAlan Cox * documentation and/or other materials provided with the distribution. 17f8a47341SAlan Cox * 18f8a47341SAlan Cox * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19f8a47341SAlan Cox * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20f8a47341SAlan Cox * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21f8a47341SAlan Cox * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22f8a47341SAlan Cox * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23f8a47341SAlan Cox * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24f8a47341SAlan Cox * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 25f8a47341SAlan Cox * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 26f8a47341SAlan Cox * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27f8a47341SAlan Cox * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 28f8a47341SAlan Cox * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29f8a47341SAlan Cox * POSSIBILITY OF SUCH DAMAGE. 30f8a47341SAlan Cox */ 31f8a47341SAlan Cox 32f8a47341SAlan Cox /* 33f8a47341SAlan Cox * Superpage reservation management module 34c68c3537SAlan Cox * 35c68c3537SAlan Cox * Any external functions defined by this module are only to be used by the 36c68c3537SAlan Cox * virtual memory system. 37f8a47341SAlan Cox */ 38f8a47341SAlan Cox 39f8a47341SAlan Cox #include <sys/cdefs.h> 40f8a47341SAlan Cox __FBSDID("$FreeBSD$"); 41f8a47341SAlan Cox 42f8a47341SAlan Cox #include "opt_vm.h" 43f8a47341SAlan Cox 44f8a47341SAlan Cox #include <sys/param.h> 45f8a47341SAlan Cox #include <sys/kernel.h> 46f8a47341SAlan Cox #include <sys/lock.h> 47f8a47341SAlan Cox #include <sys/malloc.h> 48f8a47341SAlan Cox #include <sys/mutex.h> 49f8a47341SAlan Cox #include <sys/queue.h> 50f8a47341SAlan Cox #include <sys/sbuf.h> 51f8a47341SAlan Cox #include <sys/sysctl.h> 52f8a47341SAlan Cox #include <sys/systm.h> 53f8a47341SAlan Cox 54f8a47341SAlan Cox #include <vm/vm.h> 55f8a47341SAlan Cox #include <vm/vm_param.h> 56f8a47341SAlan Cox #include <vm/vm_object.h> 57f8a47341SAlan Cox #include <vm/vm_page.h> 58f8a47341SAlan Cox #include <vm/vm_phys.h> 59f8a47341SAlan Cox #include <vm/vm_reserv.h> 60f8a47341SAlan Cox 61f8a47341SAlan Cox /* 62f8a47341SAlan Cox * The reservation system supports the speculative allocation of large physical 63f8a47341SAlan Cox * pages ("superpages"). Speculative allocation enables the fully-automatic 64f8a47341SAlan Cox * utilization of superpages by the virtual memory system. In other words, no 65f8a47341SAlan Cox * programmatic directives are required to use superpages. 66f8a47341SAlan Cox */ 67f8a47341SAlan Cox 68f8a47341SAlan Cox #if VM_NRESERVLEVEL > 0 69f8a47341SAlan Cox 70f8a47341SAlan Cox /* 71f8a47341SAlan Cox * The number of small pages that are contained in a level 0 reservation 72f8a47341SAlan Cox */ 73f8a47341SAlan Cox #define VM_LEVEL_0_NPAGES (1 << VM_LEVEL_0_ORDER) 74f8a47341SAlan Cox 75f8a47341SAlan Cox /* 76f8a47341SAlan Cox * The number of bits by which a physical address is shifted to obtain the 77f8a47341SAlan Cox * reservation number 78f8a47341SAlan Cox */ 79f8a47341SAlan Cox #define VM_LEVEL_0_SHIFT (VM_LEVEL_0_ORDER + PAGE_SHIFT) 80f8a47341SAlan Cox 81f8a47341SAlan Cox /* 82f8a47341SAlan Cox * The size of a level 0 reservation in bytes 83f8a47341SAlan Cox */ 84f8a47341SAlan Cox #define VM_LEVEL_0_SIZE (1 << VM_LEVEL_0_SHIFT) 85f8a47341SAlan Cox 86f8a47341SAlan Cox /* 87f8a47341SAlan Cox * Computes the index of the small page underlying the given (object, pindex) 88f8a47341SAlan Cox * within the reservation's array of small pages. 89f8a47341SAlan Cox */ 90f8a47341SAlan Cox #define VM_RESERV_INDEX(object, pindex) \ 91f8a47341SAlan Cox (((object)->pg_color + (pindex)) & (VM_LEVEL_0_NPAGES - 1)) 92f8a47341SAlan Cox 93f8a47341SAlan Cox /* 94f8a47341SAlan Cox * The reservation structure 95f8a47341SAlan Cox * 96f8a47341SAlan Cox * A reservation structure is constructed whenever a large physical page is 97f8a47341SAlan Cox * speculatively allocated to an object. The reservation provides the small 98f8a47341SAlan Cox * physical pages for the range [pindex, pindex + VM_LEVEL_0_NPAGES) of offsets 99f8a47341SAlan Cox * within that object. The reservation's "popcnt" tracks the number of these 100f8a47341SAlan Cox * small physical pages that are in use at any given time. When and if the 101f8a47341SAlan Cox * reservation is not fully utilized, it appears in the queue of partially- 102f8a47341SAlan Cox * populated reservations. The reservation always appears on the containing 103f8a47341SAlan Cox * object's list of reservations. 104f8a47341SAlan Cox * 105f8a47341SAlan Cox * A partially-populated reservation can be broken and reclaimed at any time. 106f8a47341SAlan Cox */ 107f8a47341SAlan Cox struct vm_reserv { 108f8a47341SAlan Cox TAILQ_ENTRY(vm_reserv) partpopq; 109f8a47341SAlan Cox LIST_ENTRY(vm_reserv) objq; 110f8a47341SAlan Cox vm_object_t object; /* containing object */ 111f8a47341SAlan Cox vm_pindex_t pindex; /* offset within object */ 112f8a47341SAlan Cox vm_page_t pages; /* first page of a superpage */ 113f8a47341SAlan Cox int popcnt; /* # of pages in use */ 114f8a47341SAlan Cox char inpartpopq; 115f8a47341SAlan Cox }; 116f8a47341SAlan Cox 117f8a47341SAlan Cox /* 118f8a47341SAlan Cox * The reservation array 119f8a47341SAlan Cox * 120f8a47341SAlan Cox * This array is analoguous in function to vm_page_array. It differs in the 121f8a47341SAlan Cox * respect that it may contain a greater number of useful reservation 122f8a47341SAlan Cox * structures than there are (physical) superpages. These "invalid" 123f8a47341SAlan Cox * reservation structures exist to trade-off space for time in the 124f8a47341SAlan Cox * implementation of vm_reserv_from_page(). Invalid reservation structures are 125f8a47341SAlan Cox * distinguishable from "valid" reservation structures by inspecting the 126f8a47341SAlan Cox * reservation's "pages" field. Invalid reservation structures have a NULL 127f8a47341SAlan Cox * "pages" field. 128f8a47341SAlan Cox * 129f8a47341SAlan Cox * vm_reserv_from_page() maps a small (physical) page to an element of this 130f8a47341SAlan Cox * array by computing a physical reservation number from the page's physical 131f8a47341SAlan Cox * address. The physical reservation number is used as the array index. 132f8a47341SAlan Cox * 133f8a47341SAlan Cox * An "active" reservation is a valid reservation structure that has a non-NULL 134f8a47341SAlan Cox * "object" field and a non-zero "popcnt" field. In other words, every active 135f8a47341SAlan Cox * reservation belongs to a particular object. Moreover, every active 136f8a47341SAlan Cox * reservation has an entry in the containing object's list of reservations. 137f8a47341SAlan Cox */ 138f8a47341SAlan Cox static vm_reserv_t vm_reserv_array; 139f8a47341SAlan Cox 140f8a47341SAlan Cox /* 141f8a47341SAlan Cox * The partially-populated reservation queue 142f8a47341SAlan Cox * 143f8a47341SAlan Cox * This queue enables the fast recovery of an unused cached or free small page 144ab5378cfSAlan Cox * from a partially-populated reservation. The reservation at the head of 145ab5378cfSAlan Cox * this queue is the least-recently-changed, partially-populated reservation. 146f8a47341SAlan Cox * 147f8a47341SAlan Cox * Access to this queue is synchronized by the free page queue lock. 148f8a47341SAlan Cox */ 149f8a47341SAlan Cox static TAILQ_HEAD(, vm_reserv) vm_rvq_partpop = 150f8a47341SAlan Cox TAILQ_HEAD_INITIALIZER(vm_rvq_partpop); 151f8a47341SAlan Cox 152f8a47341SAlan Cox static SYSCTL_NODE(_vm, OID_AUTO, reserv, CTLFLAG_RD, 0, "Reservation Info"); 153f8a47341SAlan Cox 154f8a47341SAlan Cox static long vm_reserv_broken; 155f8a47341SAlan Cox SYSCTL_LONG(_vm_reserv, OID_AUTO, broken, CTLFLAG_RD, 156f8a47341SAlan Cox &vm_reserv_broken, 0, "Cumulative number of broken reservations"); 157f8a47341SAlan Cox 158f8a47341SAlan Cox static long vm_reserv_freed; 159f8a47341SAlan Cox SYSCTL_LONG(_vm_reserv, OID_AUTO, freed, CTLFLAG_RD, 160f8a47341SAlan Cox &vm_reserv_freed, 0, "Cumulative number of freed reservations"); 161f8a47341SAlan Cox 162f8a47341SAlan Cox static int sysctl_vm_reserv_partpopq(SYSCTL_HANDLER_ARGS); 163f8a47341SAlan Cox 164f8a47341SAlan Cox SYSCTL_OID(_vm_reserv, OID_AUTO, partpopq, CTLTYPE_STRING | CTLFLAG_RD, NULL, 0, 165f8a47341SAlan Cox sysctl_vm_reserv_partpopq, "A", "Partially-populated reservation queues"); 166f8a47341SAlan Cox 167f8a47341SAlan Cox static long vm_reserv_reclaimed; 168f8a47341SAlan Cox SYSCTL_LONG(_vm_reserv, OID_AUTO, reclaimed, CTLFLAG_RD, 169f8a47341SAlan Cox &vm_reserv_reclaimed, 0, "Cumulative number of reclaimed reservations"); 170f8a47341SAlan Cox 171f8a47341SAlan Cox static void vm_reserv_depopulate(vm_reserv_t rv); 172f8a47341SAlan Cox static vm_reserv_t vm_reserv_from_page(vm_page_t m); 173f8a47341SAlan Cox static boolean_t vm_reserv_has_pindex(vm_reserv_t rv, 174f8a47341SAlan Cox vm_pindex_t pindex); 175f8a47341SAlan Cox static void vm_reserv_populate(vm_reserv_t rv); 17644aab2c3SAlan Cox static void vm_reserv_reclaim(vm_reserv_t rv); 177f8a47341SAlan Cox 178f8a47341SAlan Cox /* 179f8a47341SAlan Cox * Describes the current state of the partially-populated reservation queue. 180f8a47341SAlan Cox */ 181f8a47341SAlan Cox static int 182f8a47341SAlan Cox sysctl_vm_reserv_partpopq(SYSCTL_HANDLER_ARGS) 183f8a47341SAlan Cox { 184f8a47341SAlan Cox struct sbuf sbuf; 185f8a47341SAlan Cox vm_reserv_t rv; 186f8a47341SAlan Cox int counter, error, level, unused_pages; 187f8a47341SAlan Cox 18800f0e671SMatthew D Fleming error = sysctl_wire_old_buffer(req, 0); 18900f0e671SMatthew D Fleming if (error != 0) 19000f0e671SMatthew D Fleming return (error); 1914e657159SMatthew D Fleming sbuf_new_for_sysctl(&sbuf, NULL, 128, req); 192f8a47341SAlan Cox sbuf_printf(&sbuf, "\nLEVEL SIZE NUMBER\n\n"); 193f8a47341SAlan Cox for (level = -1; level <= VM_NRESERVLEVEL - 2; level++) { 194f8a47341SAlan Cox counter = 0; 195f8a47341SAlan Cox unused_pages = 0; 196f8a47341SAlan Cox mtx_lock(&vm_page_queue_free_mtx); 197f8a47341SAlan Cox TAILQ_FOREACH(rv, &vm_rvq_partpop/*[level]*/, partpopq) { 198f8a47341SAlan Cox counter++; 199f8a47341SAlan Cox unused_pages += VM_LEVEL_0_NPAGES - rv->popcnt; 200f8a47341SAlan Cox } 201f8a47341SAlan Cox mtx_unlock(&vm_page_queue_free_mtx); 202d689bc00SAlan Cox sbuf_printf(&sbuf, "%5d: %6dK, %6d\n", level, 2032cf36c8fSAlan Cox unused_pages * ((int)PAGE_SIZE / 1024), counter); 204f8a47341SAlan Cox } 2054e657159SMatthew D Fleming error = sbuf_finish(&sbuf); 206f8a47341SAlan Cox sbuf_delete(&sbuf); 207f8a47341SAlan Cox return (error); 208f8a47341SAlan Cox } 209f8a47341SAlan Cox 210f8a47341SAlan Cox /* 211f8a47341SAlan Cox * Reduces the given reservation's population count. If the population count 212f8a47341SAlan Cox * becomes zero, the reservation is destroyed. Additionally, moves the 213ab5378cfSAlan Cox * reservation to the tail of the partially-populated reservations queue if the 214f8a47341SAlan Cox * population count is non-zero. 215f8a47341SAlan Cox * 216f8a47341SAlan Cox * The free page queue lock must be held. 217f8a47341SAlan Cox */ 218f8a47341SAlan Cox static void 219f8a47341SAlan Cox vm_reserv_depopulate(vm_reserv_t rv) 220f8a47341SAlan Cox { 221f8a47341SAlan Cox 222f8a47341SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 223f8a47341SAlan Cox KASSERT(rv->object != NULL, 224f8a47341SAlan Cox ("vm_reserv_depopulate: reserv %p is free", rv)); 225f8a47341SAlan Cox KASSERT(rv->popcnt > 0, 226f8a47341SAlan Cox ("vm_reserv_depopulate: reserv %p's popcnt is corrupted", rv)); 227f8a47341SAlan Cox if (rv->inpartpopq) { 228f8a47341SAlan Cox TAILQ_REMOVE(&vm_rvq_partpop, rv, partpopq); 229f8a47341SAlan Cox rv->inpartpopq = FALSE; 230f8a47341SAlan Cox } 231f8a47341SAlan Cox rv->popcnt--; 232f8a47341SAlan Cox if (rv->popcnt == 0) { 233f8a47341SAlan Cox LIST_REMOVE(rv, objq); 234f8a47341SAlan Cox rv->object = NULL; 235f8a47341SAlan Cox vm_phys_free_pages(rv->pages, VM_LEVEL_0_ORDER); 236f8a47341SAlan Cox vm_reserv_freed++; 237f8a47341SAlan Cox } else { 238f8a47341SAlan Cox rv->inpartpopq = TRUE; 239ab5378cfSAlan Cox TAILQ_INSERT_TAIL(&vm_rvq_partpop, rv, partpopq); 240f8a47341SAlan Cox } 241f8a47341SAlan Cox } 242f8a47341SAlan Cox 243f8a47341SAlan Cox /* 244f8a47341SAlan Cox * Returns the reservation to which the given page might belong. 245f8a47341SAlan Cox */ 246f8a47341SAlan Cox static __inline vm_reserv_t 247f8a47341SAlan Cox vm_reserv_from_page(vm_page_t m) 248f8a47341SAlan Cox { 249f8a47341SAlan Cox 250f8a47341SAlan Cox return (&vm_reserv_array[VM_PAGE_TO_PHYS(m) >> VM_LEVEL_0_SHIFT]); 251f8a47341SAlan Cox } 252f8a47341SAlan Cox 253f8a47341SAlan Cox /* 254f8a47341SAlan Cox * Returns TRUE if the given reservation contains the given page index and 255f8a47341SAlan Cox * FALSE otherwise. 256f8a47341SAlan Cox */ 257f8a47341SAlan Cox static __inline boolean_t 258f8a47341SAlan Cox vm_reserv_has_pindex(vm_reserv_t rv, vm_pindex_t pindex) 259f8a47341SAlan Cox { 260f8a47341SAlan Cox 261f8a47341SAlan Cox return (((pindex - rv->pindex) & ~(VM_LEVEL_0_NPAGES - 1)) == 0); 262f8a47341SAlan Cox } 263f8a47341SAlan Cox 264f8a47341SAlan Cox /* 265f8a47341SAlan Cox * Increases the given reservation's population count. Moves the reservation 266f8a47341SAlan Cox * to the tail of the partially-populated reservation queue. 267f8a47341SAlan Cox * 268f8a47341SAlan Cox * The free page queue must be locked. 269f8a47341SAlan Cox */ 270f8a47341SAlan Cox static void 271f8a47341SAlan Cox vm_reserv_populate(vm_reserv_t rv) 272f8a47341SAlan Cox { 273f8a47341SAlan Cox 274f8a47341SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 275f8a47341SAlan Cox KASSERT(rv->object != NULL, 276f8a47341SAlan Cox ("vm_reserv_populate: reserv %p is free", rv)); 277f8a47341SAlan Cox KASSERT(rv->popcnt < VM_LEVEL_0_NPAGES, 278f8a47341SAlan Cox ("vm_reserv_populate: reserv %p is already full", rv)); 279f8a47341SAlan Cox if (rv->inpartpopq) { 280f8a47341SAlan Cox TAILQ_REMOVE(&vm_rvq_partpop, rv, partpopq); 281f8a47341SAlan Cox rv->inpartpopq = FALSE; 282f8a47341SAlan Cox } 283f8a47341SAlan Cox rv->popcnt++; 284f8a47341SAlan Cox if (rv->popcnt < VM_LEVEL_0_NPAGES) { 285f8a47341SAlan Cox rv->inpartpopq = TRUE; 286f8a47341SAlan Cox TAILQ_INSERT_TAIL(&vm_rvq_partpop, rv, partpopq); 287f8a47341SAlan Cox } 288f8a47341SAlan Cox } 289f8a47341SAlan Cox 290f8a47341SAlan Cox /* 291c68c3537SAlan Cox * Allocates a contiguous set of physical pages of the given size "npages" 292c68c3537SAlan Cox * from an existing or newly-created reservation. All of the physical pages 293c68c3537SAlan Cox * must be at or above the given physical address "low" and below the given 294c68c3537SAlan Cox * physical address "high". The given value "alignment" determines the 295c68c3537SAlan Cox * alignment of the first physical page in the set. If the given value 296c68c3537SAlan Cox * "boundary" is non-zero, then the set of physical pages cannot cross any 297c68c3537SAlan Cox * physical address boundary that is a multiple of that value. Both 298c68c3537SAlan Cox * "alignment" and "boundary" must be a power of two. 299c68c3537SAlan Cox * 300c68c3537SAlan Cox * The object and free page queue must be locked. 301c68c3537SAlan Cox */ 302c68c3537SAlan Cox vm_page_t 303c68c3537SAlan Cox vm_reserv_alloc_contig(vm_object_t object, vm_pindex_t pindex, u_long npages, 304c68c3537SAlan Cox vm_paddr_t low, vm_paddr_t high, u_long alignment, vm_paddr_t boundary) 305c68c3537SAlan Cox { 306c68c3537SAlan Cox vm_paddr_t pa, size; 307c68c3537SAlan Cox vm_page_t m, m_ret, mpred, msucc; 308c68c3537SAlan Cox vm_pindex_t first, leftcap, rightcap; 309c68c3537SAlan Cox vm_reserv_t rv; 310c68c3537SAlan Cox u_long allocpages, maxpages, minpages; 311c68c3537SAlan Cox int i, index, n; 312c68c3537SAlan Cox 313c68c3537SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 314c68c3537SAlan Cox VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); 315c68c3537SAlan Cox KASSERT(npages != 0, ("vm_reserv_alloc_contig: npages is 0")); 316c68c3537SAlan Cox 317c68c3537SAlan Cox /* 318c68c3537SAlan Cox * Is a reservation fundamentally impossible? 319c68c3537SAlan Cox */ 320c68c3537SAlan Cox if (pindex < VM_RESERV_INDEX(object, pindex) || 321c68c3537SAlan Cox pindex + npages > object->size) 322c68c3537SAlan Cox return (NULL); 323c68c3537SAlan Cox 324c68c3537SAlan Cox /* 325c68c3537SAlan Cox * All reservations of a particular size have the same alignment. 326c68c3537SAlan Cox * Assuming that the first page is allocated from a reservation, the 327c68c3537SAlan Cox * least significant bits of its physical address can be determined 328c68c3537SAlan Cox * from its offset from the beginning of the reservation and the size 329c68c3537SAlan Cox * of the reservation. 330c68c3537SAlan Cox * 331c68c3537SAlan Cox * Could the specified index within a reservation of the smallest 332c68c3537SAlan Cox * possible size satisfy the alignment and boundary requirements? 333c68c3537SAlan Cox */ 334c68c3537SAlan Cox pa = VM_RESERV_INDEX(object, pindex) << PAGE_SHIFT; 335c68c3537SAlan Cox if ((pa & (alignment - 1)) != 0) 336c68c3537SAlan Cox return (NULL); 337c68c3537SAlan Cox size = npages << PAGE_SHIFT; 338c68c3537SAlan Cox if (((pa ^ (pa + size - 1)) & ~(boundary - 1)) != 0) 339c68c3537SAlan Cox return (NULL); 340c68c3537SAlan Cox 341c68c3537SAlan Cox /* 342c68c3537SAlan Cox * Look for an existing reservation. 343c68c3537SAlan Cox */ 344c68c3537SAlan Cox msucc = NULL; 345c68c3537SAlan Cox mpred = object->root; 346c68c3537SAlan Cox while (mpred != NULL) { 347c68c3537SAlan Cox KASSERT(mpred->pindex != pindex, 348c68c3537SAlan Cox ("vm_reserv_alloc_contig: pindex already allocated")); 349c68c3537SAlan Cox rv = vm_reserv_from_page(mpred); 350c68c3537SAlan Cox if (rv->object == object && vm_reserv_has_pindex(rv, pindex)) 351c68c3537SAlan Cox goto found; 352c68c3537SAlan Cox else if (mpred->pindex < pindex) { 353c68c3537SAlan Cox if (msucc != NULL || 354c68c3537SAlan Cox (msucc = TAILQ_NEXT(mpred, listq)) == NULL) 355c68c3537SAlan Cox break; 356c68c3537SAlan Cox KASSERT(msucc->pindex != pindex, 357c68c3537SAlan Cox ("vm_reserv_alloc_contig: pindex already allocated")); 358c68c3537SAlan Cox rv = vm_reserv_from_page(msucc); 359c68c3537SAlan Cox if (rv->object == object && 360c68c3537SAlan Cox vm_reserv_has_pindex(rv, pindex)) 361c68c3537SAlan Cox goto found; 362c68c3537SAlan Cox else if (pindex < msucc->pindex) 363c68c3537SAlan Cox break; 364c68c3537SAlan Cox } else if (msucc == NULL) { 365c68c3537SAlan Cox msucc = mpred; 366c68c3537SAlan Cox mpred = TAILQ_PREV(msucc, pglist, listq); 367c68c3537SAlan Cox continue; 368c68c3537SAlan Cox } 369c68c3537SAlan Cox msucc = NULL; 370c68c3537SAlan Cox mpred = object->root = vm_page_splay(pindex, object->root); 371c68c3537SAlan Cox } 372c68c3537SAlan Cox 373c68c3537SAlan Cox /* 374c68c3537SAlan Cox * Could at least one reservation fit between the first index to the 375c68c3537SAlan Cox * left that can be used and the first index to the right that cannot 376c68c3537SAlan Cox * be used? 377c68c3537SAlan Cox */ 378c68c3537SAlan Cox first = pindex - VM_RESERV_INDEX(object, pindex); 379c68c3537SAlan Cox if (mpred != NULL) { 380c68c3537SAlan Cox if ((rv = vm_reserv_from_page(mpred))->object != object) 381c68c3537SAlan Cox leftcap = mpred->pindex + 1; 382c68c3537SAlan Cox else 383c68c3537SAlan Cox leftcap = rv->pindex + VM_LEVEL_0_NPAGES; 384c68c3537SAlan Cox if (leftcap > first) 385c68c3537SAlan Cox return (NULL); 386c68c3537SAlan Cox } 387c68c3537SAlan Cox minpages = VM_RESERV_INDEX(object, pindex) + npages; 388c68c3537SAlan Cox maxpages = roundup2(minpages, VM_LEVEL_0_NPAGES); 389c68c3537SAlan Cox allocpages = maxpages; 390c68c3537SAlan Cox if (msucc != NULL) { 391c68c3537SAlan Cox if ((rv = vm_reserv_from_page(msucc))->object != object) 392c68c3537SAlan Cox rightcap = msucc->pindex; 393c68c3537SAlan Cox else 394c68c3537SAlan Cox rightcap = rv->pindex; 395c68c3537SAlan Cox if (first + maxpages > rightcap) { 396c68c3537SAlan Cox if (maxpages == VM_LEVEL_0_NPAGES) 397c68c3537SAlan Cox return (NULL); 398c68c3537SAlan Cox allocpages = minpages; 399c68c3537SAlan Cox } 400c68c3537SAlan Cox } 401c68c3537SAlan Cox 402c68c3537SAlan Cox /* 403c68c3537SAlan Cox * Would the last new reservation extend past the end of the object? 404c68c3537SAlan Cox */ 405c68c3537SAlan Cox if (first + maxpages > object->size) { 406c68c3537SAlan Cox /* 407c68c3537SAlan Cox * Don't allocate the last new reservation if the object is a 408c68c3537SAlan Cox * vnode or backed by another object that is a vnode. 409c68c3537SAlan Cox */ 410c68c3537SAlan Cox if (object->type == OBJT_VNODE || 411c68c3537SAlan Cox (object->backing_object != NULL && 412c68c3537SAlan Cox object->backing_object->type == OBJT_VNODE)) { 413c68c3537SAlan Cox if (maxpages == VM_LEVEL_0_NPAGES) 414c68c3537SAlan Cox return (NULL); 415c68c3537SAlan Cox allocpages = minpages; 416c68c3537SAlan Cox } 417c68c3537SAlan Cox /* Speculate that the object may grow. */ 418c68c3537SAlan Cox } 419c68c3537SAlan Cox 420c68c3537SAlan Cox /* 421c68c3537SAlan Cox * Allocate and populate the new reservations. The alignment and 422c68c3537SAlan Cox * boundary specified for this allocation may be different from the 423c68c3537SAlan Cox * alignment and boundary specified for the requested pages. For 424c68c3537SAlan Cox * instance, the specified index may not be the first page within the 425c68c3537SAlan Cox * first new reservation. 426c68c3537SAlan Cox */ 427c68c3537SAlan Cox m = vm_phys_alloc_contig(allocpages, low, high, ulmax(alignment, 428c68c3537SAlan Cox VM_LEVEL_0_SIZE), boundary > VM_LEVEL_0_SIZE ? boundary : 0); 429c68c3537SAlan Cox if (m == NULL) 430c68c3537SAlan Cox return (NULL); 431c68c3537SAlan Cox m_ret = NULL; 432c68c3537SAlan Cox index = VM_RESERV_INDEX(object, pindex); 433c68c3537SAlan Cox do { 434c68c3537SAlan Cox rv = vm_reserv_from_page(m); 435c68c3537SAlan Cox KASSERT(rv->pages == m, 436c68c3537SAlan Cox ("vm_reserv_alloc_contig: reserv %p's pages is corrupted", 437c68c3537SAlan Cox rv)); 438c68c3537SAlan Cox KASSERT(rv->object == NULL, 439c68c3537SAlan Cox ("vm_reserv_alloc_contig: reserv %p isn't free", rv)); 440c68c3537SAlan Cox LIST_INSERT_HEAD(&object->rvq, rv, objq); 441c68c3537SAlan Cox rv->object = object; 442c68c3537SAlan Cox rv->pindex = first; 443c68c3537SAlan Cox KASSERT(rv->popcnt == 0, 444c68c3537SAlan Cox ("vm_reserv_alloc_contig: reserv %p's popcnt is corrupted", 445c68c3537SAlan Cox rv)); 446c68c3537SAlan Cox KASSERT(!rv->inpartpopq, 447c68c3537SAlan Cox ("vm_reserv_alloc_contig: reserv %p's inpartpopq is TRUE", 448c68c3537SAlan Cox rv)); 449c68c3537SAlan Cox n = ulmin(VM_LEVEL_0_NPAGES - index, npages); 450c68c3537SAlan Cox for (i = 0; i < n; i++) 451c68c3537SAlan Cox vm_reserv_populate(rv); 452c68c3537SAlan Cox npages -= n; 453c68c3537SAlan Cox if (m_ret == NULL) { 454c68c3537SAlan Cox m_ret = &rv->pages[index]; 455c68c3537SAlan Cox index = 0; 456c68c3537SAlan Cox } 457c68c3537SAlan Cox m += VM_LEVEL_0_NPAGES; 458c68c3537SAlan Cox first += VM_LEVEL_0_NPAGES; 459c68c3537SAlan Cox allocpages -= VM_LEVEL_0_NPAGES; 460c68c3537SAlan Cox } while (allocpages > VM_LEVEL_0_NPAGES); 461c68c3537SAlan Cox return (m_ret); 462c68c3537SAlan Cox 463c68c3537SAlan Cox /* 464c68c3537SAlan Cox * Found a matching reservation. 465c68c3537SAlan Cox */ 466c68c3537SAlan Cox found: 467c68c3537SAlan Cox index = VM_RESERV_INDEX(object, pindex); 468c68c3537SAlan Cox /* Does the allocation fit within the reservation? */ 469c68c3537SAlan Cox if (index + npages > VM_LEVEL_0_NPAGES) 470c68c3537SAlan Cox return (NULL); 471c68c3537SAlan Cox m = &rv->pages[index]; 472c68c3537SAlan Cox pa = VM_PAGE_TO_PHYS(m); 473c68c3537SAlan Cox if (pa < low || pa + size > high || (pa & (alignment - 1)) != 0 || 474c68c3537SAlan Cox ((pa ^ (pa + size - 1)) & ~(boundary - 1)) != 0) 475c68c3537SAlan Cox return (NULL); 476c68c3537SAlan Cox /* Handle vm_page_rename(m, new_object, ...). */ 477c68c3537SAlan Cox for (i = 0; i < npages; i++) 478c68c3537SAlan Cox if ((rv->pages[index + i].flags & (PG_CACHED | PG_FREE)) == 0) 479c68c3537SAlan Cox return (NULL); 480c68c3537SAlan Cox for (i = 0; i < npages; i++) 481c68c3537SAlan Cox vm_reserv_populate(rv); 482c68c3537SAlan Cox return (m); 483c68c3537SAlan Cox } 484c68c3537SAlan Cox 485c68c3537SAlan Cox /* 486f8a47341SAlan Cox * Allocates a page from an existing or newly-created reservation. 487f8a47341SAlan Cox * 488f8a47341SAlan Cox * The object and free page queue must be locked. 489f8a47341SAlan Cox */ 490f8a47341SAlan Cox vm_page_t 491f8a47341SAlan Cox vm_reserv_alloc_page(vm_object_t object, vm_pindex_t pindex) 492f8a47341SAlan Cox { 493f8a47341SAlan Cox vm_page_t m, mpred, msucc; 494f8a47341SAlan Cox vm_pindex_t first, leftcap, rightcap; 495f8a47341SAlan Cox vm_reserv_t rv; 496f8a47341SAlan Cox 497f8a47341SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 498c68c3537SAlan Cox VM_OBJECT_LOCK_ASSERT(object, MA_OWNED); 499f8a47341SAlan Cox 500f8a47341SAlan Cox /* 501c68c3537SAlan Cox * Is a reservation fundamentally impossible? 502f8a47341SAlan Cox */ 503f8a47341SAlan Cox if (pindex < VM_RESERV_INDEX(object, pindex) || 504f8a47341SAlan Cox pindex >= object->size) 505f8a47341SAlan Cox return (NULL); 506f8a47341SAlan Cox 507f8a47341SAlan Cox /* 508f8a47341SAlan Cox * Look for an existing reservation. 509f8a47341SAlan Cox */ 510f8a47341SAlan Cox msucc = NULL; 511f8a47341SAlan Cox mpred = object->root; 512f8a47341SAlan Cox while (mpred != NULL) { 513f8a47341SAlan Cox KASSERT(mpred->pindex != pindex, 514f8a47341SAlan Cox ("vm_reserv_alloc_page: pindex already allocated")); 515f8a47341SAlan Cox rv = vm_reserv_from_page(mpred); 516c68c3537SAlan Cox if (rv->object == object && vm_reserv_has_pindex(rv, pindex)) 517c68c3537SAlan Cox goto found; 518c68c3537SAlan Cox else if (mpred->pindex < pindex) { 519f8a47341SAlan Cox if (msucc != NULL || 520f8a47341SAlan Cox (msucc = TAILQ_NEXT(mpred, listq)) == NULL) 521f8a47341SAlan Cox break; 522f8a47341SAlan Cox KASSERT(msucc->pindex != pindex, 523f8a47341SAlan Cox ("vm_reserv_alloc_page: pindex already allocated")); 524f8a47341SAlan Cox rv = vm_reserv_from_page(msucc); 525f8a47341SAlan Cox if (rv->object == object && 526c68c3537SAlan Cox vm_reserv_has_pindex(rv, pindex)) 527c68c3537SAlan Cox goto found; 528c68c3537SAlan Cox else if (pindex < msucc->pindex) 529f8a47341SAlan Cox break; 530f8a47341SAlan Cox } else if (msucc == NULL) { 531f8a47341SAlan Cox msucc = mpred; 532f8a47341SAlan Cox mpred = TAILQ_PREV(msucc, pglist, listq); 533f8a47341SAlan Cox continue; 534f8a47341SAlan Cox } 535f8a47341SAlan Cox msucc = NULL; 536f8a47341SAlan Cox mpred = object->root = vm_page_splay(pindex, object->root); 537f8a47341SAlan Cox } 538f8a47341SAlan Cox 539f8a47341SAlan Cox /* 540c68c3537SAlan Cox * Could a reservation fit between the first index to the left that 541c68c3537SAlan Cox * can be used and the first index to the right that cannot be used? 542f8a47341SAlan Cox */ 543c68c3537SAlan Cox first = pindex - VM_RESERV_INDEX(object, pindex); 544c68c3537SAlan Cox if (mpred != NULL) { 545c68c3537SAlan Cox if ((rv = vm_reserv_from_page(mpred))->object != object) 546f8a47341SAlan Cox leftcap = mpred->pindex + 1; 547f8a47341SAlan Cox else 548f8a47341SAlan Cox leftcap = rv->pindex + VM_LEVEL_0_NPAGES; 549c68c3537SAlan Cox if (leftcap > first) 550c68c3537SAlan Cox return (NULL); 551c68c3537SAlan Cox } 552c68c3537SAlan Cox if (msucc != NULL) { 553c68c3537SAlan Cox if ((rv = vm_reserv_from_page(msucc))->object != object) 554f8a47341SAlan Cox rightcap = msucc->pindex; 555f8a47341SAlan Cox else 556f8a47341SAlan Cox rightcap = rv->pindex; 557c68c3537SAlan Cox if (first + VM_LEVEL_0_NPAGES > rightcap) 558f8a47341SAlan Cox return (NULL); 559c68c3537SAlan Cox } 560f8a47341SAlan Cox 561f8a47341SAlan Cox /* 562c68c3537SAlan Cox * Would a new reservation extend past the end of the object? 563f8a47341SAlan Cox */ 564c68c3537SAlan Cox if (first + VM_LEVEL_0_NPAGES > object->size) { 565f8a47341SAlan Cox /* 566f8a47341SAlan Cox * Don't allocate a new reservation if the object is a vnode or 567f8a47341SAlan Cox * backed by another object that is a vnode. 568f8a47341SAlan Cox */ 569f8a47341SAlan Cox if (object->type == OBJT_VNODE || 570f8a47341SAlan Cox (object->backing_object != NULL && 571f8a47341SAlan Cox object->backing_object->type == OBJT_VNODE)) 572f8a47341SAlan Cox return (NULL); 573f8a47341SAlan Cox /* Speculate that the object may grow. */ 574f8a47341SAlan Cox } 575f8a47341SAlan Cox 576f8a47341SAlan Cox /* 577c68c3537SAlan Cox * Allocate and populate the new reservation. 578f8a47341SAlan Cox */ 579f8a47341SAlan Cox m = vm_phys_alloc_pages(VM_FREEPOOL_DEFAULT, VM_LEVEL_0_ORDER); 580c68c3537SAlan Cox if (m == NULL) 581c68c3537SAlan Cox return (NULL); 582f8a47341SAlan Cox rv = vm_reserv_from_page(m); 583f8a47341SAlan Cox KASSERT(rv->pages == m, 584c68c3537SAlan Cox ("vm_reserv_alloc_page: reserv %p's pages is corrupted", rv)); 585f8a47341SAlan Cox KASSERT(rv->object == NULL, 586f8a47341SAlan Cox ("vm_reserv_alloc_page: reserv %p isn't free", rv)); 587f8a47341SAlan Cox LIST_INSERT_HEAD(&object->rvq, rv, objq); 588f8a47341SAlan Cox rv->object = object; 589f8a47341SAlan Cox rv->pindex = first; 590f8a47341SAlan Cox KASSERT(rv->popcnt == 0, 591c68c3537SAlan Cox ("vm_reserv_alloc_page: reserv %p's popcnt is corrupted", rv)); 592f8a47341SAlan Cox KASSERT(!rv->inpartpopq, 593c68c3537SAlan Cox ("vm_reserv_alloc_page: reserv %p's inpartpopq is TRUE", rv)); 594f8a47341SAlan Cox vm_reserv_populate(rv); 595c68c3537SAlan Cox return (&rv->pages[VM_RESERV_INDEX(object, pindex)]); 596c68c3537SAlan Cox 597c68c3537SAlan Cox /* 598c68c3537SAlan Cox * Found a matching reservation. 599c68c3537SAlan Cox */ 600c68c3537SAlan Cox found: 601f8a47341SAlan Cox m = &rv->pages[VM_RESERV_INDEX(object, pindex)]; 602c68c3537SAlan Cox /* Handle vm_page_rename(m, new_object, ...). */ 603c68c3537SAlan Cox if ((m->flags & (PG_CACHED | PG_FREE)) == 0) 604c68c3537SAlan Cox return (NULL); 605c68c3537SAlan Cox vm_reserv_populate(rv); 606f8a47341SAlan Cox return (m); 607f8a47341SAlan Cox } 608f8a47341SAlan Cox 609f8a47341SAlan Cox /* 610f8a47341SAlan Cox * Breaks all reservations belonging to the given object. 611f8a47341SAlan Cox */ 612f8a47341SAlan Cox void 613f8a47341SAlan Cox vm_reserv_break_all(vm_object_t object) 614f8a47341SAlan Cox { 615f8a47341SAlan Cox vm_reserv_t rv; 616f8a47341SAlan Cox int i; 617f8a47341SAlan Cox 618f8a47341SAlan Cox mtx_lock(&vm_page_queue_free_mtx); 619f8a47341SAlan Cox while ((rv = LIST_FIRST(&object->rvq)) != NULL) { 620f8a47341SAlan Cox KASSERT(rv->object == object, 621f8a47341SAlan Cox ("vm_reserv_break_all: reserv %p is corrupted", rv)); 622f8a47341SAlan Cox if (rv->inpartpopq) { 623f8a47341SAlan Cox TAILQ_REMOVE(&vm_rvq_partpop, rv, partpopq); 624f8a47341SAlan Cox rv->inpartpopq = FALSE; 625f8a47341SAlan Cox } 626f8a47341SAlan Cox LIST_REMOVE(rv, objq); 627f8a47341SAlan Cox rv->object = NULL; 628f8a47341SAlan Cox for (i = 0; i < VM_LEVEL_0_NPAGES; i++) { 629f8a47341SAlan Cox if ((rv->pages[i].flags & (PG_CACHED | PG_FREE)) != 0) 630f8a47341SAlan Cox vm_phys_free_pages(&rv->pages[i], 0); 631f8a47341SAlan Cox else 632f8a47341SAlan Cox rv->popcnt--; 633f8a47341SAlan Cox } 634f8a47341SAlan Cox KASSERT(rv->popcnt == 0, 635f8a47341SAlan Cox ("vm_reserv_break_all: reserv %p's popcnt is corrupted", 636f8a47341SAlan Cox rv)); 637f8a47341SAlan Cox vm_reserv_broken++; 638f8a47341SAlan Cox } 639f8a47341SAlan Cox mtx_unlock(&vm_page_queue_free_mtx); 640f8a47341SAlan Cox } 641f8a47341SAlan Cox 642f8a47341SAlan Cox /* 643f8a47341SAlan Cox * Frees the given page if it belongs to a reservation. Returns TRUE if the 644f8a47341SAlan Cox * page is freed and FALSE otherwise. 645f8a47341SAlan Cox * 646f8a47341SAlan Cox * The free page queue lock must be held. 647f8a47341SAlan Cox */ 648f8a47341SAlan Cox boolean_t 649f8a47341SAlan Cox vm_reserv_free_page(vm_page_t m) 650f8a47341SAlan Cox { 651f8a47341SAlan Cox vm_reserv_t rv; 652f8a47341SAlan Cox 653f8a47341SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 654f8a47341SAlan Cox rv = vm_reserv_from_page(m); 655*908e3da1SAlan Cox if (rv->object == NULL) 656*908e3da1SAlan Cox return (FALSE); 657*908e3da1SAlan Cox if ((m->flags & PG_CACHED) != 0 && m->pool != VM_FREEPOOL_CACHE) 658*908e3da1SAlan Cox vm_phys_set_pool(VM_FREEPOOL_CACHE, rv->pages, 659*908e3da1SAlan Cox VM_LEVEL_0_ORDER); 660f8a47341SAlan Cox vm_reserv_depopulate(rv); 661f8a47341SAlan Cox return (TRUE); 662f8a47341SAlan Cox } 663f8a47341SAlan Cox 664f8a47341SAlan Cox /* 665f8a47341SAlan Cox * Initializes the reservation management system. Specifically, initializes 666f8a47341SAlan Cox * the reservation array. 667f8a47341SAlan Cox * 668f8a47341SAlan Cox * Requires that vm_page_array and first_page are initialized! 669f8a47341SAlan Cox */ 670f8a47341SAlan Cox void 671f8a47341SAlan Cox vm_reserv_init(void) 672f8a47341SAlan Cox { 673f8a47341SAlan Cox vm_paddr_t paddr; 674f8a47341SAlan Cox int i; 675f8a47341SAlan Cox 676f8a47341SAlan Cox /* 677f8a47341SAlan Cox * Initialize the reservation array. Specifically, initialize the 678f8a47341SAlan Cox * "pages" field for every element that has an underlying superpage. 679f8a47341SAlan Cox */ 680f8a47341SAlan Cox for (i = 0; phys_avail[i + 1] != 0; i += 2) { 681f8a47341SAlan Cox paddr = roundup2(phys_avail[i], VM_LEVEL_0_SIZE); 682f8a47341SAlan Cox while (paddr + VM_LEVEL_0_SIZE <= phys_avail[i + 1]) { 683f8a47341SAlan Cox vm_reserv_array[paddr >> VM_LEVEL_0_SHIFT].pages = 684f8a47341SAlan Cox PHYS_TO_VM_PAGE(paddr); 685f8a47341SAlan Cox paddr += VM_LEVEL_0_SIZE; 686f8a47341SAlan Cox } 687f8a47341SAlan Cox } 688f8a47341SAlan Cox } 689f8a47341SAlan Cox 690f8a47341SAlan Cox /* 691f8a47341SAlan Cox * Returns a reservation level if the given page belongs to a fully-populated 692f8a47341SAlan Cox * reservation and -1 otherwise. 693f8a47341SAlan Cox */ 694f8a47341SAlan Cox int 695f8a47341SAlan Cox vm_reserv_level_iffullpop(vm_page_t m) 696f8a47341SAlan Cox { 697f8a47341SAlan Cox vm_reserv_t rv; 698f8a47341SAlan Cox 699f8a47341SAlan Cox rv = vm_reserv_from_page(m); 700f8a47341SAlan Cox return (rv->popcnt == VM_LEVEL_0_NPAGES ? 0 : -1); 701f8a47341SAlan Cox } 702f8a47341SAlan Cox 703f8a47341SAlan Cox /* 704f8a47341SAlan Cox * Prepare for the reactivation of a cached page. 705f8a47341SAlan Cox * 706f8a47341SAlan Cox * First, suppose that the given page "m" was allocated individually, i.e., not 707f8a47341SAlan Cox * as part of a reservation, and cached. Then, suppose a reservation 708f8a47341SAlan Cox * containing "m" is allocated by the same object. Although "m" and the 709f8a47341SAlan Cox * reservation belong to the same object, "m"'s pindex may not match the 710f8a47341SAlan Cox * reservation's. 711f8a47341SAlan Cox * 712f8a47341SAlan Cox * The free page queue must be locked. 713f8a47341SAlan Cox */ 714f8a47341SAlan Cox boolean_t 715f8a47341SAlan Cox vm_reserv_reactivate_page(vm_page_t m) 716f8a47341SAlan Cox { 717f8a47341SAlan Cox vm_reserv_t rv; 718f8a47341SAlan Cox int i, m_index; 719f8a47341SAlan Cox 720f8a47341SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 721f8a47341SAlan Cox rv = vm_reserv_from_page(m); 722f8a47341SAlan Cox if (rv->object == NULL) 723f8a47341SAlan Cox return (FALSE); 724f8a47341SAlan Cox KASSERT((m->flags & PG_CACHED) != 0, 725f8a47341SAlan Cox ("vm_reserv_uncache_page: page %p is not cached", m)); 726f8a47341SAlan Cox if (m->object == rv->object && 727f8a47341SAlan Cox m->pindex - rv->pindex == VM_RESERV_INDEX(m->object, m->pindex)) 728f8a47341SAlan Cox vm_reserv_populate(rv); 729f8a47341SAlan Cox else { 730f8a47341SAlan Cox KASSERT(rv->inpartpopq, 731f8a47341SAlan Cox ("vm_reserv_uncache_page: reserv %p's inpartpopq is FALSE", 732f8a47341SAlan Cox rv)); 733f8a47341SAlan Cox TAILQ_REMOVE(&vm_rvq_partpop, rv, partpopq); 734f8a47341SAlan Cox rv->inpartpopq = FALSE; 735f8a47341SAlan Cox LIST_REMOVE(rv, objq); 736f8a47341SAlan Cox rv->object = NULL; 737f8a47341SAlan Cox /* Don't vm_phys_free_pages(m, 0). */ 738f8a47341SAlan Cox m_index = m - rv->pages; 739f8a47341SAlan Cox for (i = 0; i < m_index; i++) { 740f8a47341SAlan Cox if ((rv->pages[i].flags & (PG_CACHED | PG_FREE)) != 0) 741f8a47341SAlan Cox vm_phys_free_pages(&rv->pages[i], 0); 742f8a47341SAlan Cox else 743f8a47341SAlan Cox rv->popcnt--; 744f8a47341SAlan Cox } 745f8a47341SAlan Cox for (i++; i < VM_LEVEL_0_NPAGES; i++) { 746f8a47341SAlan Cox if ((rv->pages[i].flags & (PG_CACHED | PG_FREE)) != 0) 747f8a47341SAlan Cox vm_phys_free_pages(&rv->pages[i], 0); 748f8a47341SAlan Cox else 749f8a47341SAlan Cox rv->popcnt--; 750f8a47341SAlan Cox } 751f8a47341SAlan Cox KASSERT(rv->popcnt == 0, 752f8a47341SAlan Cox ("vm_reserv_uncache_page: reserv %p's popcnt is corrupted", 753f8a47341SAlan Cox rv)); 754f8a47341SAlan Cox vm_reserv_broken++; 755f8a47341SAlan Cox } 756f8a47341SAlan Cox return (TRUE); 757f8a47341SAlan Cox } 758f8a47341SAlan Cox 759f8a47341SAlan Cox /* 76044aab2c3SAlan Cox * Breaks the given partially-populated reservation, releasing its cached and 76144aab2c3SAlan Cox * free pages to the physical memory allocator. 762f8a47341SAlan Cox * 763f8a47341SAlan Cox * The free page queue lock must be held. 764f8a47341SAlan Cox */ 76544aab2c3SAlan Cox static void 76644aab2c3SAlan Cox vm_reserv_reclaim(vm_reserv_t rv) 767f8a47341SAlan Cox { 768f8a47341SAlan Cox int i; 769f8a47341SAlan Cox 770f8a47341SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 771f8a47341SAlan Cox KASSERT(rv->inpartpopq, 77244aab2c3SAlan Cox ("vm_reserv_reclaim: reserv %p's inpartpopq is corrupted", rv)); 773f8a47341SAlan Cox TAILQ_REMOVE(&vm_rvq_partpop, rv, partpopq); 774f8a47341SAlan Cox rv->inpartpopq = FALSE; 775f8a47341SAlan Cox KASSERT(rv->object != NULL, 776f8a47341SAlan Cox ("vm_reserv_reclaim: reserv %p is free", rv)); 777f8a47341SAlan Cox LIST_REMOVE(rv, objq); 778f8a47341SAlan Cox rv->object = NULL; 779f8a47341SAlan Cox for (i = 0; i < VM_LEVEL_0_NPAGES; i++) { 780f8a47341SAlan Cox if ((rv->pages[i].flags & (PG_CACHED | PG_FREE)) != 0) 781f8a47341SAlan Cox vm_phys_free_pages(&rv->pages[i], 0); 782f8a47341SAlan Cox else 783f8a47341SAlan Cox rv->popcnt--; 784f8a47341SAlan Cox } 785f8a47341SAlan Cox KASSERT(rv->popcnt == 0, 78644aab2c3SAlan Cox ("vm_reserv_reclaim: reserv %p's popcnt is corrupted", rv)); 787f8a47341SAlan Cox vm_reserv_reclaimed++; 78844aab2c3SAlan Cox } 78944aab2c3SAlan Cox 79044aab2c3SAlan Cox /* 79144aab2c3SAlan Cox * Breaks the reservation at the head of the partially-populated reservation 79244aab2c3SAlan Cox * queue, releasing its cached and free pages to the physical memory 79344aab2c3SAlan Cox * allocator. Returns TRUE if a reservation is broken and FALSE otherwise. 79444aab2c3SAlan Cox * 79544aab2c3SAlan Cox * The free page queue lock must be held. 79644aab2c3SAlan Cox */ 79744aab2c3SAlan Cox boolean_t 79844aab2c3SAlan Cox vm_reserv_reclaim_inactive(void) 79944aab2c3SAlan Cox { 80044aab2c3SAlan Cox vm_reserv_t rv; 80144aab2c3SAlan Cox 80244aab2c3SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 80344aab2c3SAlan Cox if ((rv = TAILQ_FIRST(&vm_rvq_partpop)) != NULL) { 80444aab2c3SAlan Cox vm_reserv_reclaim(rv); 805f8a47341SAlan Cox return (TRUE); 806f8a47341SAlan Cox } 807f8a47341SAlan Cox return (FALSE); 808f8a47341SAlan Cox } 809f8a47341SAlan Cox 810f8a47341SAlan Cox /* 81144aab2c3SAlan Cox * Searches the partially-populated reservation queue for the least recently 81244aab2c3SAlan Cox * active reservation with unused pages, i.e., cached or free, that satisfy the 81344aab2c3SAlan Cox * given request for contiguous physical memory. If a satisfactory reservation 81444aab2c3SAlan Cox * is found, it is broken. Returns TRUE if a reservation is broken and FALSE 81544aab2c3SAlan Cox * otherwise. 81644aab2c3SAlan Cox * 81744aab2c3SAlan Cox * The free page queue lock must be held. 81844aab2c3SAlan Cox */ 81944aab2c3SAlan Cox boolean_t 820c68c3537SAlan Cox vm_reserv_reclaim_contig(u_long npages, vm_paddr_t low, vm_paddr_t high, 8215c1f2cc4SAlan Cox u_long alignment, vm_paddr_t boundary) 82244aab2c3SAlan Cox { 823c68c3537SAlan Cox vm_paddr_t pa, pa_length, size; 82444aab2c3SAlan Cox vm_reserv_t rv; 82544aab2c3SAlan Cox int i; 82644aab2c3SAlan Cox 82744aab2c3SAlan Cox mtx_assert(&vm_page_queue_free_mtx, MA_OWNED); 828c68c3537SAlan Cox if (npages > VM_LEVEL_0_NPAGES - 1) 82944aab2c3SAlan Cox return (FALSE); 830c68c3537SAlan Cox size = npages << PAGE_SHIFT; 83144aab2c3SAlan Cox TAILQ_FOREACH(rv, &vm_rvq_partpop, partpopq) { 83244aab2c3SAlan Cox pa = VM_PAGE_TO_PHYS(&rv->pages[VM_LEVEL_0_NPAGES - 1]); 83344aab2c3SAlan Cox if (pa + PAGE_SIZE - size < low) { 83444aab2c3SAlan Cox /* this entire reservation is too low; go to next */ 83544aab2c3SAlan Cox continue; 83644aab2c3SAlan Cox } 83744aab2c3SAlan Cox pa_length = 0; 83844aab2c3SAlan Cox for (i = 0; i < VM_LEVEL_0_NPAGES; i++) 83944aab2c3SAlan Cox if ((rv->pages[i].flags & (PG_CACHED | PG_FREE)) != 0) { 84044aab2c3SAlan Cox pa_length += PAGE_SIZE; 84144aab2c3SAlan Cox if (pa_length == PAGE_SIZE) { 84244aab2c3SAlan Cox pa = VM_PAGE_TO_PHYS(&rv->pages[i]); 84344aab2c3SAlan Cox if (pa + size > high) { 84444aab2c3SAlan Cox /* skip to next reservation */ 84544aab2c3SAlan Cox break; 84644aab2c3SAlan Cox } else if (pa < low || 84744aab2c3SAlan Cox (pa & (alignment - 1)) != 0 || 84844aab2c3SAlan Cox ((pa ^ (pa + size - 1)) & 84944aab2c3SAlan Cox ~(boundary - 1)) != 0) 85044aab2c3SAlan Cox pa_length = 0; 85185f2a0c9SMax Laier } 85285f2a0c9SMax Laier if (pa_length >= size) { 85344aab2c3SAlan Cox vm_reserv_reclaim(rv); 85444aab2c3SAlan Cox return (TRUE); 85544aab2c3SAlan Cox } 85644aab2c3SAlan Cox } else 85744aab2c3SAlan Cox pa_length = 0; 85844aab2c3SAlan Cox } 85944aab2c3SAlan Cox return (FALSE); 86044aab2c3SAlan Cox } 86144aab2c3SAlan Cox 86244aab2c3SAlan Cox /* 863f8a47341SAlan Cox * Transfers the reservation underlying the given page to a new object. 864f8a47341SAlan Cox * 865f8a47341SAlan Cox * The object must be locked. 866f8a47341SAlan Cox */ 867f8a47341SAlan Cox void 868f8a47341SAlan Cox vm_reserv_rename(vm_page_t m, vm_object_t new_object, vm_object_t old_object, 869f8a47341SAlan Cox vm_pindex_t old_object_offset) 870f8a47341SAlan Cox { 871f8a47341SAlan Cox vm_reserv_t rv; 872f8a47341SAlan Cox 873f8a47341SAlan Cox VM_OBJECT_LOCK_ASSERT(new_object, MA_OWNED); 874f8a47341SAlan Cox rv = vm_reserv_from_page(m); 875f8a47341SAlan Cox if (rv->object == old_object) { 876f8a47341SAlan Cox mtx_lock(&vm_page_queue_free_mtx); 877f8a47341SAlan Cox if (rv->object == old_object) { 878f8a47341SAlan Cox LIST_REMOVE(rv, objq); 879f8a47341SAlan Cox LIST_INSERT_HEAD(&new_object->rvq, rv, objq); 880f8a47341SAlan Cox rv->object = new_object; 881f8a47341SAlan Cox rv->pindex -= old_object_offset; 882f8a47341SAlan Cox } 883f8a47341SAlan Cox mtx_unlock(&vm_page_queue_free_mtx); 884f8a47341SAlan Cox } 885f8a47341SAlan Cox } 886f8a47341SAlan Cox 887f8a47341SAlan Cox /* 888f8a47341SAlan Cox * Allocates the virtual and physical memory required by the reservation 889f8a47341SAlan Cox * management system's data structures, in particular, the reservation array. 890f8a47341SAlan Cox */ 891f8a47341SAlan Cox vm_paddr_t 892f8a47341SAlan Cox vm_reserv_startup(vm_offset_t *vaddr, vm_paddr_t end, vm_paddr_t high_water) 893f8a47341SAlan Cox { 894f8a47341SAlan Cox vm_paddr_t new_end; 895f8a47341SAlan Cox size_t size; 896f8a47341SAlan Cox 897f8a47341SAlan Cox /* 898f8a47341SAlan Cox * Calculate the size (in bytes) of the reservation array. Round up 899f8a47341SAlan Cox * from "high_water" because every small page is mapped to an element 900f8a47341SAlan Cox * in the reservation array based on its physical address. Thus, the 901f8a47341SAlan Cox * number of elements in the reservation array can be greater than the 902f8a47341SAlan Cox * number of superpages. 903f8a47341SAlan Cox */ 904f8a47341SAlan Cox size = howmany(high_water, VM_LEVEL_0_SIZE) * sizeof(struct vm_reserv); 905f8a47341SAlan Cox 906f8a47341SAlan Cox /* 907f8a47341SAlan Cox * Allocate and map the physical memory for the reservation array. The 908f8a47341SAlan Cox * next available virtual address is returned by reference. 909f8a47341SAlan Cox */ 910f8a47341SAlan Cox new_end = end - round_page(size); 911f8a47341SAlan Cox vm_reserv_array = (void *)(uintptr_t)pmap_map(vaddr, new_end, end, 912f8a47341SAlan Cox VM_PROT_READ | VM_PROT_WRITE); 913f8a47341SAlan Cox bzero(vm_reserv_array, size); 914f8a47341SAlan Cox 915f8a47341SAlan Cox /* 916f8a47341SAlan Cox * Return the next available physical address. 917f8a47341SAlan Cox */ 918f8a47341SAlan Cox return (new_end); 919f8a47341SAlan Cox } 920f8a47341SAlan Cox 921f8a47341SAlan Cox #endif /* VM_NRESERVLEVEL > 0 */ 922