diff options
-rw-r--r-- | sys/uvm/uvm_pglist.c | 188 |
1 files changed, 107 insertions, 81 deletions
diff --git a/sys/uvm/uvm_pglist.c b/sys/uvm/uvm_pglist.c index 8fc67464371..cbfe7b8f5b0 100644 --- a/sys/uvm/uvm_pglist.c +++ b/sys/uvm/uvm_pglist.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uvm_pglist.c,v 1.22 2008/06/26 05:42:20 ray Exp $ */ +/* $OpenBSD: uvm_pglist.c,v 1.23 2008/06/27 17:25:47 miod Exp $ */ /* $NetBSD: uvm_pglist.c,v 1.13 2001/02/18 21:19:08 chs Exp $ */ /*- @@ -58,14 +58,20 @@ u_long uvm_pglistalloc_npages; int uvm_pglistalloc_simple(psize_t, paddr_t, paddr_t, struct pglist *); +/* + * Simple page allocation: pages do not need to be contiguous. We just + * attempt to find enough free pages in the given range. + */ int uvm_pglistalloc_simple(psize_t size, paddr_t low, paddr_t high, struct pglist *rlist) { - psize_t try; + psize_t todo; int psi; struct vm_page *pg; - int todo, idx, pgflidx, error, free_list; + struct vm_physseg *seg; + paddr_t slow, shigh; + int pgflidx, error, free_list; UVMHIST_FUNC("uvm_pglistalloc_simple"); UVMHIST_CALLED(pghist); #ifdef DEBUG vm_page_t tp; @@ -74,7 +80,7 @@ uvm_pglistalloc_simple(psize_t size, paddr_t low, paddr_t high, /* Default to "lose". */ error = ENOMEM; - todo = size / PAGE_SIZE; + todo = atop(size); /* * Block all memory allocation and lock the free list. @@ -85,44 +91,56 @@ uvm_pglistalloc_simple(psize_t size, paddr_t low, paddr_t high, if (uvmexp.free <= (uvmexp.reserve_pagedaemon + uvmexp.reserve_kernel)) goto out; - for (try = low; try < high; try += PAGE_SIZE) { - + for (psi = 0, seg = vm_physmem; psi < vm_nphysseg; psi++, seg++) { /* - * Make sure this is a managed physical page. + * Skip this segment if incompatible with the address range. */ + if (seg->avail_end <= atop(low)) + continue; + if (seg->avail_start >= atop(high)) + continue; + + slow = MAX(atop(low), seg->avail_start); + shigh = MIN(atop(high), seg->avail_end); - if ((psi = vm_physseg_find(atop(try), &idx)) == -1) - continue; /* managed? */ - pg = &vm_physmem[psi].pgs[idx]; - if (VM_PAGE_IS_FREE(pg) == 0) + /* we want to be able to allocate at least a page... */ + if (slow == shigh) continue; - free_list = uvm_page_lookup_freelist(pg); - pgflidx = (pg->pg_flags & PG_ZERO) ? PGFL_ZEROS : PGFL_UNKNOWN; + for (pg = &seg->pgs[slow - seg->start]; slow != shigh; + slow++, pg++) { + if (VM_PAGE_IS_FREE(pg) == 0) + continue; + + free_list = uvm_page_lookup_freelist(pg); + pgflidx = (pg->pg_flags & PG_ZERO) ? + PGFL_ZEROS : PGFL_UNKNOWN; #ifdef DEBUG - for (tp = TAILQ_FIRST(&uvm.page_free[free_list].pgfl_queues[pgflidx]); - tp != NULL; - tp = TAILQ_NEXT(tp, pageq)) { - if (tp == pg) - break; - } - if (tp == NULL) - panic("uvm_pglistalloc_simple: page not on freelist"); + for (tp = TAILQ_FIRST(&uvm.page_free[free_list].pgfl_queues[pgflidx]); + tp != NULL; tp = TAILQ_NEXT(tp, pageq)) { + if (tp == pg) + break; + } + if (tp == NULL) + panic("uvm_pglistalloc_simple: page not on freelist"); #endif - TAILQ_REMOVE(&uvm.page_free[free_list].pgfl_queues[pgflidx], pg, pageq); - uvmexp.free--; - if (pg->pg_flags & PG_ZERO) - uvmexp.zeropages--; - pg->pg_flags = PG_CLEAN; - pg->uobject = NULL; - pg->uanon = NULL; - pg->pg_version++; - TAILQ_INSERT_TAIL(rlist, pg, pageq); - STAT_INCR(uvm_pglistalloc_npages); - if (--todo == 0) { - error = 0; - goto out; + TAILQ_REMOVE(&uvm.page_free[free_list].pgfl_queues[pgflidx], + pg, pageq); + uvmexp.free--; + if (pg->pg_flags & PG_ZERO) + uvmexp.zeropages--; + pg->pg_flags = PG_CLEAN; + pg->uobject = NULL; + pg->uanon = NULL; + pg->pg_version++; + TAILQ_INSERT_TAIL(rlist, pg, pageq); + STAT_INCR(uvm_pglistalloc_npages); + if (--todo == 0) { + error = 0; + goto out; + } } + } out: @@ -171,10 +189,12 @@ uvm_pglistalloc(size, low, high, alignment, boundary, rlist, nsegs, waitok) struct pglist *rlist; int nsegs, waitok; { - paddr_t try, idxpa, lastidxpa; int psi; struct vm_page *pgs; - int tryidx, idx, pgflidx, end, error, free_list; + struct vm_physseg *seg; + paddr_t slow, shigh; + paddr_t try, idxpa, lastidxpa; + int tryidx, idx, pgflidx, endidx, error, free_list; vm_page_t m; u_long pagemask; #ifdef DEBUG @@ -196,11 +216,15 @@ uvm_pglistalloc(size, low, high, alignment, boundary, rlist, nsegs, waitok) return (EINVAL); size = round_page(size); - try = roundup(low, alignment); + low = roundup(low, alignment); + /* + * If we are allowed to allocate as many segments as pages, + * no need to be smart. + */ if ((nsegs >= size / PAGE_SIZE) && (alignment == PAGE_SIZE) && (boundary == 0)) - return (uvm_pglistalloc_simple(size, try, high, rlist)); + return (uvm_pglistalloc_simple(size, low, high, rlist)); if (boundary != 0 && boundary < size) return (EINVAL); @@ -219,61 +243,62 @@ uvm_pglistalloc(size, low, high, alignment, boundary, rlist, nsegs, waitok) if (uvmexp.free <= (uvmexp.reserve_pagedaemon + uvmexp.reserve_kernel)) goto out; - for (;; try += alignment) { - if (try + size > high) { - - /* - * We've run past the allowable range. - */ - - goto out; - } - + for (psi = 0, seg = vm_physmem; psi < vm_nphysseg; psi++, seg++) { /* - * Make sure this is a managed physical page. + * Skip this segment if incompatible with the address range. */ + if (seg->avail_end <= atop(low)) + continue; + if (seg->avail_start >= atop(high)) + continue; - if ((psi = vm_physseg_find(atop(try), &idx)) == -1) - continue; /* managed? */ - if (vm_physseg_find(atop(try + size), NULL) != psi) - continue; /* end must be in this segment */ - - tryidx = idx; - end = idx + (size / PAGE_SIZE); - pgs = vm_physmem[psi].pgs; - - /* - * Found a suitable starting page. See of the range is free. - */ + slow = MAX(low, ptoa(seg->avail_start)); + shigh = MIN(high, ptoa(seg->avail_end)); - for (; idx < end; idx++) { - if (VM_PAGE_IS_FREE(&pgs[idx]) == 0) { + try = roundup(slow, alignment); + for (;; try += alignment) { + if (try + size > shigh) { + /* + * We've run past the allowable range, or + * the segment. Try another. + */ break; } - idxpa = VM_PAGE_TO_PHYS(&pgs[idx]); - if (idx > tryidx) { - lastidxpa = VM_PAGE_TO_PHYS(&pgs[idx - 1]); - if ((lastidxpa + PAGE_SIZE) != idxpa) { - /* - * Region not contiguous. - */ + tryidx = idx = atop(try) - seg->start; + endidx = idx + atop(size); + pgs = vm_physmem[psi].pgs; + /* + * Found a suitable starting page. See if the + * range is free. + */ + + for (; idx < endidx; idx++) { + if (VM_PAGE_IS_FREE(&pgs[idx]) == 0) { break; } - if (boundary != 0 && - ((lastidxpa ^ idxpa) & pagemask) != 0) { - - /* - * Region crosses boundary. - */ + idxpa = VM_PAGE_TO_PHYS(&pgs[idx]); + if (idx == tryidx) + continue; + + /* + * Check that the region is contiguous + * (it really should...) and does not + * cross an alignment boundary. + */ + lastidxpa = VM_PAGE_TO_PHYS(&pgs[idx - 1]); + if ((lastidxpa + PAGE_SIZE) != idxpa) + break; + if (boundary != 0 && + ((lastidxpa ^ idxpa) & pagemask) != 0) break; - } } - } - if (idx == end) { - break; + + if (idx == endidx) { + goto found; + } } } @@ -281,11 +306,12 @@ uvm_pglistalloc(size, low, high, alignment, boundary, rlist, nsegs, waitok) #error uvm_pglistalloc needs to be updated #endif +found: /* * we have a chunk of memory that conforms to the requested constraints. */ idx = tryidx; - while (idx < end) { + while (idx < endidx) { m = &pgs[idx]; free_list = uvm_page_lookup_freelist(m); pgflidx = (m->pg_flags & PG_ZERO) ? PGFL_ZEROS : PGFL_UNKNOWN; |