Annotation of sys/uvm/uvm_pglist.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: uvm_pglist.c,v 1.20 2007/04/13 18:57:49 art Exp $ */
! 2: /* $NetBSD: uvm_pglist.c,v 1.13 2001/02/18 21:19:08 chs Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 1997 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
! 10: * NASA Ames Research Center.
! 11: *
! 12: * Redistribution and use in source and binary forms, with or without
! 13: * modification, are permitted provided that the following conditions
! 14: * are met:
! 15: * 1. Redistributions of source code must retain the above copyright
! 16: * notice, this list of conditions and the following disclaimer.
! 17: * 2. Redistributions in binary form must reproduce the above copyright
! 18: * notice, this list of conditions and the following disclaimer in the
! 19: * documentation and/or other materials provided with the distribution.
! 20: * 3. All advertising materials mentioning features or use of this software
! 21: * must display the following acknowledgement:
! 22: * This product includes software developed by the NetBSD
! 23: * Foundation, Inc. and its contributors.
! 24: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 25: * contributors may be used to endorse or promote products derived
! 26: * from this software without specific prior written permission.
! 27: *
! 28: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 29: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 30: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 31: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 32: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 33: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 34: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 35: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 36: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 37: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 38: * POSSIBILITY OF SUCH DAMAGE.
! 39: */
! 40:
! 41: /*
! 42: * uvm_pglist.c: pglist functions
! 43: */
! 44:
! 45: #include <sys/param.h>
! 46: #include <sys/systm.h>
! 47: #include <sys/malloc.h>
! 48: #include <sys/proc.h>
! 49:
! 50: #include <uvm/uvm.h>
! 51:
! 52: #ifdef VM_PAGE_ALLOC_MEMORY_STATS
! 53: #define STAT_INCR(v) (v)++
! 54: #define STAT_DECR(v) do { \
! 55: if ((v) == 0) \
! 56: printf("%s:%d -- Already 0!\n", __FILE__, __LINE__); \
! 57: else \
! 58: (v)--; \
! 59: } while (0)
! 60: u_long uvm_pglistalloc_npages;
! 61: #else
! 62: #define STAT_INCR(v)
! 63: #define STAT_DECR(v)
! 64: #endif
! 65:
! 66: int uvm_pglistalloc_simple(psize_t, paddr_t, paddr_t, struct pglist *);
! 67:
! 68: int
! 69: uvm_pglistalloc_simple(psize_t size, paddr_t low, paddr_t high,
! 70: struct pglist *rlist)
! 71: {
! 72: psize_t try;
! 73: int psi;
! 74: struct vm_page *pg;
! 75: int s, todo, idx, pgflidx, error, free_list;
! 76: UVMHIST_FUNC("uvm_pglistalloc_simple"); UVMHIST_CALLED(pghist);
! 77: #ifdef DEBUG
! 78: vm_page_t tp;
! 79: #endif
! 80:
! 81: /* Default to "lose". */
! 82: error = ENOMEM;
! 83:
! 84: todo = size / PAGE_SIZE;
! 85:
! 86: /*
! 87: * Block all memory allocation and lock the free list.
! 88: */
! 89: s = uvm_lock_fpageq();
! 90:
! 91: /* Are there even any free pages? */
! 92: if (uvmexp.free <= (uvmexp.reserve_pagedaemon + uvmexp.reserve_kernel))
! 93: goto out;
! 94:
! 95: for (try = low; try < high; try += PAGE_SIZE) {
! 96:
! 97: /*
! 98: * Make sure this is a managed physical page.
! 99: */
! 100:
! 101: if ((psi = vm_physseg_find(atop(try), &idx)) == -1)
! 102: continue; /* managed? */
! 103: pg = &vm_physmem[psi].pgs[idx];
! 104: if (VM_PAGE_IS_FREE(pg) == 0)
! 105: continue;
! 106:
! 107: free_list = uvm_page_lookup_freelist(pg);
! 108: pgflidx = (pg->pg_flags & PG_ZERO) ? PGFL_ZEROS : PGFL_UNKNOWN;
! 109: #ifdef DEBUG
! 110: for (tp = TAILQ_FIRST(&uvm.page_free[free_list].pgfl_queues[pgflidx]);
! 111: tp != NULL;
! 112: tp = TAILQ_NEXT(tp, pageq)) {
! 113: if (tp == pg)
! 114: break;
! 115: }
! 116: if (tp == NULL)
! 117: panic("uvm_pglistalloc_simple: page not on freelist");
! 118: #endif
! 119: TAILQ_REMOVE(&uvm.page_free[free_list].pgfl_queues[pgflidx], pg, pageq);
! 120: uvmexp.free--;
! 121: if (pg->pg_flags & PG_ZERO)
! 122: uvmexp.zeropages--;
! 123: pg->pg_flags = PG_CLEAN;
! 124: pg->uobject = NULL;
! 125: pg->uanon = NULL;
! 126: pg->pg_version++;
! 127: TAILQ_INSERT_TAIL(rlist, pg, pageq);
! 128: STAT_INCR(uvm_pglistalloc_npages);
! 129: if (--todo == 0) {
! 130: error = 0;
! 131: goto out;
! 132: }
! 133: }
! 134:
! 135: out:
! 136: /*
! 137: * check to see if we need to generate some free pages waking
! 138: * the pagedaemon.
! 139: */
! 140:
! 141: if (!error && (uvmexp.free + uvmexp.paging < uvmexp.freemin ||
! 142: (uvmexp.free + uvmexp.paging < uvmexp.freetarg &&
! 143: uvmexp.inactive < uvmexp.inactarg))) {
! 144: wakeup(&uvm.pagedaemon);
! 145: }
! 146:
! 147: uvm_unlock_fpageq(s);
! 148:
! 149: if (error)
! 150: uvm_pglistfree(rlist);
! 151:
! 152: return (error);
! 153: }
! 154:
! 155: /*
! 156: * uvm_pglistalloc: allocate a list of pages
! 157: *
! 158: * => allocated pages are placed at the tail of rlist. rlist is
! 159: * assumed to be properly initialized by caller.
! 160: * => returns 0 on success or errno on failure
! 161: * => XXX: implementation allocates only a single segment, also
! 162: * might be able to better advantage of vm_physeg[].
! 163: * => doesn't take into account clean non-busy pages on inactive list
! 164: * that could be used(?)
! 165: * => params:
! 166: * size the size of the allocation, rounded to page size.
! 167: * low the low address of the allowed allocation range.
! 168: * high the high address of the allowed allocation range.
! 169: * alignment memory must be aligned to this power-of-two boundary.
! 170: * boundary no segment in the allocation may cross this
! 171: * power-of-two boundary (relative to zero).
! 172: */
! 173:
! 174: int
! 175: uvm_pglistalloc(size, low, high, alignment, boundary, rlist, nsegs, waitok)
! 176: psize_t size;
! 177: paddr_t low, high, alignment, boundary;
! 178: struct pglist *rlist;
! 179: int nsegs, waitok;
! 180: {
! 181: paddr_t try, idxpa, lastidxpa;
! 182: int psi;
! 183: struct vm_page *pgs;
! 184: int s, tryidx, idx, pgflidx, end, error, free_list;
! 185: vm_page_t m;
! 186: u_long pagemask;
! 187: #ifdef DEBUG
! 188: vm_page_t tp;
! 189: #endif
! 190: UVMHIST_FUNC("uvm_pglistalloc"); UVMHIST_CALLED(pghist);
! 191:
! 192: KASSERT((alignment & (alignment - 1)) == 0);
! 193: KASSERT((boundary & (boundary - 1)) == 0);
! 194:
! 195: /*
! 196: * Our allocations are always page granularity, so our alignment
! 197: * must be, too.
! 198: */
! 199: if (alignment < PAGE_SIZE)
! 200: alignment = PAGE_SIZE;
! 201:
! 202: if (size == 0)
! 203: return (EINVAL);
! 204:
! 205: size = round_page(size);
! 206: try = roundup(low, alignment);
! 207:
! 208: if ((nsegs >= size / PAGE_SIZE) && (alignment == PAGE_SIZE) &&
! 209: (boundary == 0))
! 210: return (uvm_pglistalloc_simple(size, try, high, rlist));
! 211:
! 212: if (boundary != 0 && boundary < size)
! 213: return (EINVAL);
! 214:
! 215: pagemask = ~(boundary - 1);
! 216:
! 217: /* Default to "lose". */
! 218: error = ENOMEM;
! 219:
! 220: /*
! 221: * Block all memory allocation and lock the free list.
! 222: */
! 223: s = uvm_lock_fpageq();
! 224:
! 225: /* Are there even any free pages? */
! 226: if (uvmexp.free <= (uvmexp.reserve_pagedaemon + uvmexp.reserve_kernel))
! 227: goto out;
! 228:
! 229: for (;; try += alignment) {
! 230: if (try + size > high) {
! 231:
! 232: /*
! 233: * We've run past the allowable range.
! 234: */
! 235:
! 236: goto out;
! 237: }
! 238:
! 239: /*
! 240: * Make sure this is a managed physical page.
! 241: */
! 242:
! 243: if ((psi = vm_physseg_find(atop(try), &idx)) == -1)
! 244: continue; /* managed? */
! 245: if (vm_physseg_find(atop(try + size), NULL) != psi)
! 246: continue; /* end must be in this segment */
! 247:
! 248: tryidx = idx;
! 249: end = idx + (size / PAGE_SIZE);
! 250: pgs = vm_physmem[psi].pgs;
! 251:
! 252: /*
! 253: * Found a suitable starting page. See of the range is free.
! 254: */
! 255:
! 256: for (; idx < end; idx++) {
! 257: if (VM_PAGE_IS_FREE(&pgs[idx]) == 0) {
! 258: break;
! 259: }
! 260: idxpa = VM_PAGE_TO_PHYS(&pgs[idx]);
! 261: if (idx > tryidx) {
! 262: lastidxpa = VM_PAGE_TO_PHYS(&pgs[idx - 1]);
! 263: if ((lastidxpa + PAGE_SIZE) != idxpa) {
! 264:
! 265: /*
! 266: * Region not contiguous.
! 267: */
! 268:
! 269: break;
! 270: }
! 271: if (boundary != 0 &&
! 272: ((lastidxpa ^ idxpa) & pagemask) != 0) {
! 273:
! 274: /*
! 275: * Region crosses boundary.
! 276: */
! 277:
! 278: break;
! 279: }
! 280: }
! 281: }
! 282: if (idx == end) {
! 283: break;
! 284: }
! 285: }
! 286:
! 287: #if PGFL_NQUEUES != 2
! 288: #error uvm_pglistalloc needs to be updated
! 289: #endif
! 290:
! 291: /*
! 292: * we have a chunk of memory that conforms to the requested constraints.
! 293: */
! 294: idx = tryidx;
! 295: while (idx < end) {
! 296: m = &pgs[idx];
! 297: free_list = uvm_page_lookup_freelist(m);
! 298: pgflidx = (m->pg_flags & PG_ZERO) ? PGFL_ZEROS : PGFL_UNKNOWN;
! 299: #ifdef DEBUG
! 300: for (tp = TAILQ_FIRST(&uvm.page_free[
! 301: free_list].pgfl_queues[pgflidx]);
! 302: tp != NULL;
! 303: tp = TAILQ_NEXT(tp, pageq)) {
! 304: if (tp == m)
! 305: break;
! 306: }
! 307: if (tp == NULL)
! 308: panic("uvm_pglistalloc: page not on freelist");
! 309: #endif
! 310: TAILQ_REMOVE(&uvm.page_free[free_list].pgfl_queues[pgflidx],
! 311: m, pageq);
! 312: uvmexp.free--;
! 313: if (m->pg_flags & PG_ZERO)
! 314: uvmexp.zeropages--;
! 315: m->pg_flags = PG_CLEAN;
! 316: m->uobject = NULL;
! 317: m->uanon = NULL;
! 318: m->pg_version++;
! 319: TAILQ_INSERT_TAIL(rlist, m, pageq);
! 320: idx++;
! 321: STAT_INCR(uvm_pglistalloc_npages);
! 322: }
! 323: error = 0;
! 324:
! 325: out:
! 326: /*
! 327: * check to see if we need to generate some free pages waking
! 328: * the pagedaemon.
! 329: */
! 330:
! 331: if (uvmexp.free + uvmexp.paging < uvmexp.freemin ||
! 332: (uvmexp.free + uvmexp.paging < uvmexp.freetarg &&
! 333: uvmexp.inactive < uvmexp.inactarg)) {
! 334: wakeup(&uvm.pagedaemon);
! 335: }
! 336:
! 337: uvm_unlock_fpageq(s);
! 338:
! 339: return (error);
! 340: }
! 341:
! 342: /*
! 343: * uvm_pglistfree: free a list of pages
! 344: *
! 345: * => pages should already be unmapped
! 346: */
! 347:
! 348: void
! 349: uvm_pglistfree(struct pglist *list)
! 350: {
! 351: struct vm_page *m;
! 352: int s;
! 353: UVMHIST_FUNC("uvm_pglistfree"); UVMHIST_CALLED(pghist);
! 354:
! 355: /*
! 356: * Block all memory allocation and lock the free list.
! 357: */
! 358: s = uvm_lock_fpageq();
! 359:
! 360: while ((m = TAILQ_FIRST(list)) != NULL) {
! 361: KASSERT((m->pg_flags & (PQ_ACTIVE|PQ_INACTIVE)) == 0);
! 362: TAILQ_REMOVE(list, m, pageq);
! 363: #ifdef DEBUG
! 364: if (m->uobject == (void *)0xdeadbeef &&
! 365: m->uanon == (void *)0xdeadbeef) {
! 366: panic("uvm_pagefree: freeing free page %p", m);
! 367: }
! 368:
! 369: m->uobject = (void *)0xdeadbeef;
! 370: m->offset = 0xdeadbeef;
! 371: m->uanon = (void *)0xdeadbeef;
! 372: #endif
! 373: atomic_clearbits_int(&m->pg_flags, PQ_MASK);
! 374: atomic_setbits_int(&m->pg_flags, PQ_FREE);
! 375: TAILQ_INSERT_TAIL(&uvm.page_free[
! 376: uvm_page_lookup_freelist(m)].pgfl_queues[PGFL_UNKNOWN],
! 377: m, pageq);
! 378: uvmexp.free++;
! 379: if (uvmexp.zeropages < UVM_PAGEZERO_TARGET)
! 380: uvm.page_idle_zero = vm_page_zero_enable;
! 381: STAT_DECR(uvm_pglistalloc_npages);
! 382: }
! 383:
! 384: uvm_unlock_fpageq(s);
! 385: }
CVSweb