#include <sys/param.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/mount.h>
#define VALID_FLAGS(pg_flags) \
(((pg_flags) & ~(PQ_FREE|PG_ZERO|PG_PMAPMASK)) == 0x0)
int uvm_pmemrange_addr_cmp(const struct uvm_pmemrange *,
const struct uvm_pmemrange *);
int uvm_pmemrange_use_cmp(struct uvm_pmemrange *, struct uvm_pmemrange *);
int uvm_pmr_pg_to_memtype(struct vm_page *);
#ifdef DDB
void uvm_pmr_print(void);
#endif
static inline int
in_pagedaemon(int allowsyncer)
{
if (curcpu()->ci_idepth > 0)
return 0;
if (curproc == uvm.pagedaemon_proc)
return 1;
if (allowsyncer && (curproc == syncerproc))
return 1;
return 0;
}
int
uvm_pmr_pg_to_memtype(struct vm_page *pg)
{
if (pg->pg_flags & PG_ZERO)
return UVM_PMR_MEMTYPE_ZERO;
return UVM_PMR_MEMTYPE_DIRTY;
}
RBT_GENERATE(uvm_pmr_addr, vm_page, objt, uvm_pmr_addr_cmp);
RBT_GENERATE(uvm_pmr_size, vm_page, objt, uvm_pmr_size_cmp);
RBT_GENERATE(uvm_pmemrange_addr, uvm_pmemrange, pmr_addr,
uvm_pmemrange_addr_cmp);
#ifdef DEBUG
void uvm_pmr_assertvalid(struct uvm_pmemrange *pmr);
#else
#define uvm_pmr_assertvalid(pmr) do {} while (0)
#endif
psize_t uvm_pmr_get1page(psize_t, int, struct pglist *,
paddr_t, paddr_t, int);
struct uvm_pmemrange *uvm_pmr_allocpmr(void);
struct vm_page *uvm_pmr_nfindsz(struct uvm_pmemrange *, psize_t, int);
struct vm_page *uvm_pmr_nextsz(struct uvm_pmemrange *,
struct vm_page *, int);
void uvm_pmr_pnaddr(struct uvm_pmemrange *pmr,
struct vm_page *pg, struct vm_page **pg_prev,
struct vm_page **pg_next);
struct vm_page *uvm_pmr_findnextsegment(struct uvm_pmemrange *,
struct vm_page *, paddr_t);
struct vm_page *uvm_pmr_findprevsegment(struct uvm_pmemrange *,
struct vm_page *, paddr_t);
psize_t uvm_pmr_remove_1strange(struct pglist *, paddr_t,
struct vm_page **, int);
psize_t uvm_pmr_remove_1strange_reverse(struct pglist *,
paddr_t *);
void uvm_pmr_split(paddr_t);
struct uvm_pmemrange *uvm_pmemrange_find(paddr_t);
struct uvm_pmemrange *uvm_pmemrange_use_insert(struct uvm_pmemrange_use *,
struct uvm_pmemrange *);
psize_t pow2divide(psize_t, psize_t);
struct vm_page *uvm_pmr_rootupdate(struct uvm_pmemrange *,
struct vm_page *, paddr_t, paddr_t, int);
psize_t
pow2divide(psize_t num, psize_t denom)
{
int rshift;
for (rshift = 0; num > denom; rshift++, denom <<= 1)
;
return (paddr_t)1 << rshift;
}
#define PMR_IS_SUBRANGE_OF(lhs_low, lhs_high, rhs_low, rhs_high) \
(((rhs_low) == 0 || (lhs_low) >= (rhs_low)) && \
((rhs_high) == 0 || (lhs_high) <= (rhs_high)))
#define PMR_INTERSECTS_WITH(lhs_low, lhs_high, rhs_low, rhs_high) \
(((rhs_low) == 0 || (rhs_low) < (lhs_high)) && \
((rhs_high) == 0 || (lhs_low) < (rhs_high)))
#define PMR_ALIGN(pgno, align) \
(((pgno) + ((align) - 1)) & ~((align) - 1))
#define PMR_ALIGN_DOWN(pgno, align) \
((pgno) & ~((align) - 1))
int
uvm_pmemrange_addr_cmp(const struct uvm_pmemrange *lhs,
const struct uvm_pmemrange *rhs)
{
return lhs->low < rhs->low ? -1 : lhs->low > rhs->low;
}
int
uvm_pmemrange_use_cmp(struct uvm_pmemrange *lhs, struct uvm_pmemrange *rhs)
{
int result;
result = lhs->use < rhs->use ? -1 : lhs->use > rhs->use;
if (result == 0)
result = uvm_pmemrange_addr_cmp(lhs, rhs);
return result;
}
int
uvm_pmr_addr_cmp(const struct vm_page *lhs, const struct vm_page *rhs)
{
paddr_t lhs_addr, rhs_addr;
lhs_addr = VM_PAGE_TO_PHYS(lhs);
rhs_addr = VM_PAGE_TO_PHYS(rhs);
return (lhs_addr < rhs_addr ? -1 : lhs_addr > rhs_addr);
}
int
uvm_pmr_size_cmp(const struct vm_page *lhs, const struct vm_page *rhs)
{
psize_t lhs_size, rhs_size;
int cmp;
lhs_size = (lhs - 1)->fpgsz;
rhs_size = (rhs - 1)->fpgsz;
cmp = (lhs_size < rhs_size ? -1 : lhs_size > rhs_size);
if (cmp == 0)
cmp = uvm_pmr_addr_cmp(lhs - 1, rhs - 1);
return cmp;
}
struct vm_page *
uvm_pmr_nfindsz(struct uvm_pmemrange *pmr, psize_t sz, int mti)
{
struct vm_page *node, *best;
KASSERT(sz >= 1);
if (sz == 1 && !TAILQ_EMPTY(&pmr->single[mti]))
return TAILQ_FIRST(&pmr->single[mti]);
node = RBT_ROOT(uvm_pmr_size, &pmr->size[mti]);
best = NULL;
while (node != NULL) {
if ((node - 1)->fpgsz >= sz) {
best = (node - 1);
node = RBT_LEFT(uvm_objtree, node);
} else
node = RBT_RIGHT(uvm_objtree, node);
}
return best;
}
struct vm_page *
uvm_pmr_nextsz(struct uvm_pmemrange *pmr, struct vm_page *pg, int mt)
{
struct vm_page *npg;
KASSERT(pmr != NULL && pg != NULL);
if (pg->fpgsz == 1) {
if (TAILQ_NEXT(pg, pageq) != NULL)
return TAILQ_NEXT(pg, pageq);
else
npg = RBT_MIN(uvm_pmr_size, &pmr->size[mt]);
} else
npg = RBT_NEXT(uvm_pmr_size, pg + 1);
return npg == NULL ? NULL : npg - 1;
}
void
uvm_pmr_pnaddr(struct uvm_pmemrange *pmr, struct vm_page *pg,
struct vm_page **pg_prev, struct vm_page **pg_next)
{
KASSERT(pg_prev != NULL && pg_next != NULL);
*pg_next = RBT_NFIND(uvm_pmr_addr, &pmr->addr, pg);
if (*pg_next == NULL)
*pg_prev = RBT_MAX(uvm_pmr_addr, &pmr->addr);
else
*pg_prev = RBT_PREV(uvm_pmr_addr, *pg_next);
KDASSERT(*pg_next == NULL ||
VM_PAGE_TO_PHYS(*pg_next) > VM_PAGE_TO_PHYS(pg));
KDASSERT(*pg_prev == NULL ||
VM_PAGE_TO_PHYS(*pg_prev) < VM_PAGE_TO_PHYS(pg));
if (*pg_prev != NULL &&
(atop(VM_PAGE_TO_PHYS(*pg_prev)) + (*pg_prev)->fpgsz
!= atop(VM_PAGE_TO_PHYS(pg)) ||
*pg_prev + (*pg_prev)->fpgsz != pg ||
uvm_pmr_pg_to_memtype(*pg_prev) != uvm_pmr_pg_to_memtype(pg)))
*pg_prev = NULL;
if (*pg_next != NULL &&
(atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz
!= atop(VM_PAGE_TO_PHYS(*pg_next)) ||
pg + pg->fpgsz != *pg_next ||
uvm_pmr_pg_to_memtype(*pg_next) != uvm_pmr_pg_to_memtype(pg)))
*pg_next = NULL;
return;
}
void
uvm_pmr_remove_addr(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
KDASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, pg) == pg);
KDASSERT(pg->pg_flags & PQ_FREE);
RBT_REMOVE(uvm_pmr_addr, &pmr->addr, pg);
pmr->nsegs--;
}
void
uvm_pmr_remove_size(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
int memtype;
#ifdef DEBUG
struct vm_page *i;
#endif
KDASSERT(pg->fpgsz >= 1);
KDASSERT(pg->pg_flags & PQ_FREE);
memtype = uvm_pmr_pg_to_memtype(pg);
if (pg->fpgsz == 1) {
#ifdef DEBUG
TAILQ_FOREACH(i, &pmr->single[memtype], pageq) {
if (i == pg)
break;
}
KDASSERT(i == pg);
#endif
TAILQ_REMOVE(&pmr->single[memtype], pg, pageq);
} else {
KDASSERT(RBT_FIND(uvm_pmr_size, &pmr->size[memtype],
pg + 1) == pg + 1);
RBT_REMOVE(uvm_pmr_size, &pmr->size[memtype], pg + 1);
}
}
void
uvm_pmr_remove(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
uvm_pmr_assertvalid(pmr);
uvm_pmr_remove_size(pmr, pg);
uvm_pmr_remove_addr(pmr, pg);
uvm_pmr_assertvalid(pmr);
}
struct vm_page *
uvm_pmr_insert_addr(struct uvm_pmemrange *pmr, struct vm_page *pg, int no_join)
{
struct vm_page *prev, *next;
#ifdef DEBUG
struct vm_page *i;
int mt;
#endif
KDASSERT(pg->pg_flags & PQ_FREE);
KDASSERT(pg->fpgsz >= 1);
#ifdef DEBUG
for (mt = 0; mt < UVM_PMR_MEMTYPE_MAX; mt++) {
TAILQ_FOREACH(i, &pmr->single[mt], pageq)
KDASSERT(i != pg);
if (pg->fpgsz > 1) {
KDASSERT(RBT_FIND(uvm_pmr_size, &pmr->size[mt],
pg + 1) == NULL);
}
KDASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, pg) == NULL);
}
#endif
if (!no_join) {
uvm_pmr_pnaddr(pmr, pg, &prev, &next);
if (next != NULL) {
uvm_pmr_remove_size(pmr, next);
uvm_pmr_remove_addr(pmr, next);
pg->fpgsz += next->fpgsz;
next->fpgsz = 0;
}
if (prev != NULL) {
uvm_pmr_remove_size(pmr, prev);
prev->fpgsz += pg->fpgsz;
pg->fpgsz = 0;
return prev;
}
}
RBT_INSERT(uvm_pmr_addr, &pmr->addr, pg);
pmr->nsegs++;
return pg;
}
void
uvm_pmr_insert_size(struct uvm_pmemrange *pmr, struct vm_page *pg)
{
int memtype;
#ifdef DEBUG
struct vm_page *i;
int mti;
#endif
KDASSERT(pg->fpgsz >= 1);
KDASSERT(pg->pg_flags & PQ_FREE);
memtype = uvm_pmr_pg_to_memtype(pg);
#ifdef DEBUG
for (mti = 0; mti < UVM_PMR_MEMTYPE_MAX; mti++) {
TAILQ_FOREACH(i, &pmr->single[mti], pageq)
KDASSERT(i != pg);
if (pg->fpgsz > 1) {
KDASSERT(RBT_FIND(uvm_pmr_size, &pmr->size[mti],
pg + 1) == NULL);
}
KDASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, pg) == pg);
}
for (i = pg; i < pg + pg->fpgsz; i++)
KASSERT(uvm_pmr_pg_to_memtype(i) == memtype);
#endif
if (pg->fpgsz == 1)
TAILQ_INSERT_TAIL(&pmr->single[memtype], pg, pageq);
else
RBT_INSERT(uvm_pmr_size, &pmr->size[memtype], pg + 1);
}
struct vm_page *
uvm_pmr_insert(struct uvm_pmemrange *pmr, struct vm_page *pg, int no_join)
{
uvm_pmr_assertvalid(pmr);
pg = uvm_pmr_insert_addr(pmr, pg, no_join);
uvm_pmr_insert_size(pmr, pg);
uvm_pmr_assertvalid(pmr);
return pg;
}
struct vm_page *
uvm_pmr_findnextsegment(struct uvm_pmemrange *pmr,
struct vm_page *pg, paddr_t boundary)
{
paddr_t first_boundary;
struct vm_page *next;
struct vm_page *prev;
KDASSERT(pmr->low <= atop(VM_PAGE_TO_PHYS(pg)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(pg)));
if (boundary != 0) {
first_boundary =
PMR_ALIGN(atop(VM_PAGE_TO_PHYS(pg)) + 1, boundary);
} else
first_boundary = 0;
prev = pg;
next = TAILQ_NEXT(prev, pageq);
while (next != NULL &&
(boundary == 0 || atop(VM_PAGE_TO_PHYS(next)) < first_boundary) &&
atop(VM_PAGE_TO_PHYS(prev)) + 1 == atop(VM_PAGE_TO_PHYS(next)) &&
pmr->low <= atop(VM_PAGE_TO_PHYS(next)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(next)) &&
uvm_pmr_pg_to_memtype(prev) == uvm_pmr_pg_to_memtype(next) &&
prev + 1 == next) {
prev = next;
next = TAILQ_NEXT(prev, pageq);
}
return prev;
}
struct vm_page *
uvm_pmr_findprevsegment(struct uvm_pmemrange *pmr,
struct vm_page *pg, paddr_t boundary)
{
paddr_t first_boundary;
struct vm_page *next;
struct vm_page *prev;
KDASSERT(pmr->low <= atop(VM_PAGE_TO_PHYS(pg)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(pg)));
if (boundary != 0) {
first_boundary =
PMR_ALIGN_DOWN(atop(VM_PAGE_TO_PHYS(pg)), boundary);
} else
first_boundary = 0;
prev = pg;
next = TAILQ_NEXT(prev, pageq);
while (next != NULL &&
(boundary == 0 || atop(VM_PAGE_TO_PHYS(next)) >= first_boundary) &&
atop(VM_PAGE_TO_PHYS(prev)) - 1 == atop(VM_PAGE_TO_PHYS(next)) &&
pmr->low <= atop(VM_PAGE_TO_PHYS(next)) &&
pmr->high > atop(VM_PAGE_TO_PHYS(next)) &&
uvm_pmr_pg_to_memtype(prev) == uvm_pmr_pg_to_memtype(next) &&
prev - 1 == next) {
prev = next;
next = TAILQ_NEXT(prev, pageq);
}
return prev;
}
psize_t
uvm_pmr_remove_1strange(struct pglist *pgl, paddr_t boundary,
struct vm_page **work, int is_desperate)
{
struct vm_page *start, *end, *iter, *iter_end, *inserted, *lowest;
psize_t count;
struct uvm_pmemrange *pmr, *pmr_iter;
KASSERT(!TAILQ_EMPTY(pgl));
start = TAILQ_FIRST(pgl);
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(start)));
end = uvm_pmr_findnextsegment(pmr, start, boundary);
if (is_desperate) {
pmr_iter = pmr;
for (iter = TAILQ_NEXT(end, pageq);
iter != NULL && start != end;
iter = TAILQ_NEXT(iter_end, pageq)) {
if (pmr->low > atop(VM_PAGE_TO_PHYS(iter)) ||
pmr->high <= atop(VM_PAGE_TO_PHYS(iter))) {
pmr_iter = uvm_pmemrange_find(atop(
VM_PAGE_TO_PHYS(iter)));
}
iter_end = uvm_pmr_findnextsegment(pmr_iter, iter,
boundary);
if (VM_PAGE_TO_PHYS(iter_end) - VM_PAGE_TO_PHYS(iter) <
VM_PAGE_TO_PHYS(end) - VM_PAGE_TO_PHYS(start)) {
start = iter;
end = iter_end;
pmr = pmr_iter;
}
}
}
count = atop(VM_PAGE_TO_PHYS(end) - VM_PAGE_TO_PHYS(start)) + 1;
lowest = start;
end = TAILQ_NEXT(end, pageq);
for (iter = start; iter != end; iter = iter_end) {
iter_end = TAILQ_NEXT(iter, pageq);
TAILQ_REMOVE(pgl, iter, pageq);
}
lowest->fpgsz = count;
inserted = uvm_pmr_insert(pmr, lowest, 0);
if (work != NULL && *work != NULL &&
atop(VM_PAGE_TO_PHYS(inserted)) <= atop(VM_PAGE_TO_PHYS(*work)) &&
atop(VM_PAGE_TO_PHYS(inserted)) + inserted->fpgsz >
atop(VM_PAGE_TO_PHYS(*work)))
*work = inserted;
return count;
}
psize_t
uvm_pmr_remove_1strange_reverse(struct pglist *pgl, paddr_t *pstart)
{
struct vm_page *start, *end, *iter, *iter_end, *lowest;
psize_t count;
struct uvm_pmemrange *pmr;
KASSERT(!TAILQ_EMPTY(pgl));
start = TAILQ_FIRST(pgl);
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(start)));
end = uvm_pmr_findprevsegment(pmr, start, 0);
KASSERT(end <= start);
count = atop(VM_PAGE_TO_PHYS(start) - VM_PAGE_TO_PHYS(end)) + 1;
lowest = end;
end = TAILQ_NEXT(end, pageq);
for (iter = start; iter != end; iter = iter_end) {
iter_end = TAILQ_NEXT(iter, pageq);
TAILQ_REMOVE(pgl, iter, pageq);
}
lowest->fpgsz = count;
(void) uvm_pmr_insert(pmr, lowest, 0);
*pstart = VM_PAGE_TO_PHYS(lowest);
return count;
}
struct vm_page *
uvm_pmr_extract_range(struct uvm_pmemrange *pmr, struct vm_page *pg,
paddr_t start, paddr_t end, struct pglist *result)
{
struct vm_page *after, *pg_i;
psize_t before_sz, after_sz;
#ifdef DEBUG
psize_t i;
#endif
KDASSERT(end > start);
KDASSERT(pmr->low <= atop(VM_PAGE_TO_PHYS(pg)));
KDASSERT(pmr->high >= atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz);
KDASSERT(atop(VM_PAGE_TO_PHYS(pg)) <= start);
KDASSERT(atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz >= end);
before_sz = start - atop(VM_PAGE_TO_PHYS(pg));
after_sz = atop(VM_PAGE_TO_PHYS(pg)) + pg->fpgsz - end;
KDASSERT(before_sz + after_sz + (end - start) == pg->fpgsz);
uvm_pmr_assertvalid(pmr);
uvm_pmr_remove_size(pmr, pg);
if (before_sz == 0)
uvm_pmr_remove_addr(pmr, pg);
after = pg + before_sz + (end - start);
for (pg_i = pg + before_sz; pg_i != after; pg_i++) {
KASSERT(pg_i->pg_flags & PQ_FREE);
pg_i->fpgsz = 0;
TAILQ_INSERT_TAIL(result, pg_i, pageq);
}
if (before_sz > 0) {
pg->fpgsz = before_sz;
uvm_pmr_insert_size(pmr, pg);
}
if (after_sz > 0) {
#ifdef DEBUG
for (i = 0; i < after_sz; i++) {
KASSERT(!uvm_pmr_isfree(after + i));
}
#endif
KDASSERT(atop(VM_PAGE_TO_PHYS(after)) == end);
after->fpgsz = after_sz;
after = uvm_pmr_insert_addr(pmr, after, 1);
uvm_pmr_insert_size(pmr, after);
}
uvm_pmr_assertvalid(pmr);
return (after_sz > 0 ? after : NULL);
}
int
uvm_pmr_getpages(psize_t count, paddr_t start, paddr_t end, paddr_t align,
paddr_t boundary, int maxseg, int flags, struct pglist *result)
{
struct uvm_pmemrange *pmr;
struct vm_page *found, *f_next;
psize_t fcount;
int fnsegs;
int try, start_try;
psize_t search[3];
paddr_t fstart, fend;
int memtype;
int memtype_init;
int desperate;
#ifdef DIAGNOSTIC
struct vm_page *diag_prev;
#endif
KASSERT(count > 0);
KASSERT(start == 0 || end == 0 || start < end);
KASSERT(align >= 1);
KASSERT(powerof2(align));
KASSERT(maxseg > 0);
KASSERT(boundary == 0 || powerof2(boundary));
KASSERT(boundary == 0 || maxseg * boundary >= count);
KASSERT(TAILQ_EMPTY(result));
KASSERT(!(flags & UVM_PLA_WAITOK) ^ !(flags & UVM_PLA_NOWAIT));
if (maxseg == 1 || count == 1)
flags &= ~UVM_PLA_TRYCONTIG;
if (maxseg == 1 || count == 1) {
start_try = 2;
search[2] = count;
} else if (maxseg >= count && (flags & UVM_PLA_TRYCONTIG) == 0) {
start_try = 2;
search[2] = 1;
} else {
start_try = 0;
search[0] = count;
search[1] = pow2divide(count, maxseg);
search[2] = 1;
if ((flags & UVM_PLA_TRYCONTIG) == 0)
start_try = 1;
if (search[1] >= search[0]) {
search[1] = search[0];
start_try = 1;
}
if (search[2] >= search[start_try]) {
start_try = 2;
}
}
if (flags & UVM_PLA_ZERO)
memtype_init = UVM_PMR_MEMTYPE_ZERO;
else
memtype_init = UVM_PMR_MEMTYPE_DIRTY;
desperate = 0;
uvm_lock_fpageq();
retry:
if ((atomic_load_sint(&uvmexp.free) - BUFPAGES_DEFICIT) < uvmexp.freemin ||
((atomic_load_sint(&uvmexp.free) - BUFPAGES_DEFICIT) <
atomic_load_sint(&uvmexp.freetarg) &&
(atomic_load_sint(&uvmexp.inactive) + BUFPAGES_INACT) <
atomic_load_sint(&uvmexp.inactarg)))
wakeup(&uvm.pagedaemon);
if ((atomic_load_sint(&uvmexp.free) <= uvmexp.reserve_kernel + count) &&
!(flags & UVM_PLA_USERESERVE)) {
uvm_unlock_fpageq();
return ENOMEM;
}
if ((atomic_load_sint(&uvmexp.free) <= uvmexp.reserve_pagedaemon + count) &&
!in_pagedaemon(1)) {
uvm_unlock_fpageq();
if (flags & UVM_PLA_WAITOK) {
uvm_wait("uvm_pmr_getpages");
uvm_lock_fpageq();
goto retry;
}
return ENOMEM;
}
fcount = 0;
fnsegs = 0;
retry_desperate:
if (count <= maxseg && align == 1 && boundary == 0 &&
(flags & UVM_PLA_TRYCONTIG) == 0) {
fcount += uvm_pmr_get1page(count - fcount, memtype_init,
result, start, end, 0);
if (fcount == count)
goto out;
else
goto fail;
}
TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) {
if (pmr->nsegs == 0)
continue;
if (!PMR_INTERSECTS_WITH(pmr->low, pmr->high, start, end))
continue;
memtype = memtype_init;
rescan_memtype:
try = start_try;
rescan:
for (found = uvm_pmr_nfindsz(pmr, search[try], memtype);
found != NULL;
found = f_next) {
f_next = uvm_pmr_nextsz(pmr, found, memtype);
fstart = atop(VM_PAGE_TO_PHYS(found));
if (start != 0)
fstart = MAX(start, fstart);
drain_found:
if (fnsegs == maxseg) {
fnsegs--;
fcount -=
uvm_pmr_remove_1strange(result, boundary,
&found, desperate);
}
fstart = PMR_ALIGN(fstart, align);
fend = atop(VM_PAGE_TO_PHYS(found)) + found->fpgsz;
if (end != 0)
fend = MIN(end, fend);
if (boundary != 0) {
fend =
MIN(fend, PMR_ALIGN(fstart + 1, boundary));
}
if (fstart >= fend)
continue;
if (fend - fstart > count - fcount)
fend = fstart + (count - fcount);
fcount += fend - fstart;
fnsegs++;
found = uvm_pmr_extract_range(pmr, found,
fstart, fend, result);
if (fcount == count)
goto out;
if (found != NULL) {
fstart = fend;
goto drain_found;
}
}
if (++try < nitems(search))
goto rescan;
memtype += 1;
if (memtype == UVM_PMR_MEMTYPE_MAX)
memtype = 0;
if (memtype != memtype_init)
goto rescan_memtype;
if (!desperate && TAILQ_NEXT(pmr, pmr_use) != NULL &&
TAILQ_NEXT(pmr, pmr_use)->use != pmr->use)
break;
}
if (!desperate) {
desperate = 1;
start_try = nitems(search) - 1;
flags &= ~UVM_PLA_TRYCONTIG;
while (!TAILQ_EMPTY(result))
uvm_pmr_remove_1strange(result, 0, NULL, 0);
fnsegs = 0;
fcount = 0;
goto retry_desperate;
}
fail:
while (!TAILQ_EMPTY(result))
uvm_pmr_remove_1strange(result, 0, NULL, 0);
if (flags & UVM_PLA_WAITOK) {
if (uvm_wait_pla(ptoa(start), ptoa(end) - 1, ptoa(count),
flags & UVM_PLA_FAILOK) == 0)
goto retry;
KASSERT(flags & UVM_PLA_FAILOK);
} else if (!(flags & UVM_PLA_NOWAKE)) {
struct uvm_pmalloc *pma = &nowait_pma;
if (!(nowait_pma.pm_flags & UVM_PMA_LINKED)) {
nowait_pma.pm_flags = UVM_PMA_LINKED;
TAILQ_INSERT_HEAD(&uvm.pmr_control.allocs, pma, pmq);
wakeup(&uvm.pagedaemon);
}
}
uvm_unlock_fpageq();
return ENOMEM;
out:
atomic_sub_int(&uvmexp.free, fcount);
uvm_unlock_fpageq();
#ifdef DIAGNOSTIC
fnsegs = 0;
fcount = 0;
diag_prev = NULL;
#endif
TAILQ_FOREACH(found, result, pageq) {
atomic_clearbits_int(&found->pg_flags, PG_PMAPMASK);
if (found->pg_flags & PG_ZERO) {
uvm_lock_fpageq();
atomic_dec_int(&uvmexp.zeropages);
if (atomic_load_sint(&uvmexp.zeropages) < UVM_PAGEZERO_TARGET)
wakeup(&uvmexp.zeropages);
uvm_unlock_fpageq();
}
if (flags & UVM_PLA_ZERO) {
if (found->pg_flags & PG_ZERO)
atomic_inc_int(&uvmexp.pga_zerohit);
else {
atomic_inc_int(&uvmexp.pga_zeromiss);
uvm_pagezero(found);
}
}
atomic_clearbits_int(&found->pg_flags, PG_ZERO|PQ_FREE);
KASSERT(found->uobject == NULL);
KASSERT(found->uanon == NULL);
found->pg_version++;
KDASSERT(start == 0 || atop(VM_PAGE_TO_PHYS(found)) >= start);
KDASSERT(end == 0 || atop(VM_PAGE_TO_PHYS(found)) < end);
#ifdef DIAGNOSTIC
if (diag_prev == NULL ||
atop(VM_PAGE_TO_PHYS(diag_prev)) + 1 !=
atop(VM_PAGE_TO_PHYS(found)) ||
(atop(VM_PAGE_TO_PHYS(diag_prev)) & ~(boundary - 1)) !=
(atop(VM_PAGE_TO_PHYS(found)) & ~(boundary - 1)))
fnsegs++;
fcount++;
diag_prev = found;
#endif
}
#ifdef DIAGNOSTIC
if (fcount != count || fnsegs > maxseg) {
panic("pmemrange allocation error: "
"allocated %ld pages in %d segments, "
"but request was %ld pages in %d segments",
fcount, fnsegs, count, maxseg);
}
#endif
return 0;
}
struct vm_page *
uvm_pmr_getone(int flags)
{
struct vm_page *pg;
struct pglist pgl;
TAILQ_INIT(&pgl);
if (uvm_pmr_getpages(1, 0, 0, 1, 0, 1, flags, &pgl) != 0)
return NULL;
pg = TAILQ_FIRST(&pgl);
KASSERT(pg != NULL && TAILQ_NEXT(pg, pageq) == NULL);
return pg;
}
void
uvm_pmr_freepages(struct vm_page *pg, psize_t count)
{
struct uvm_pmemrange *pmr;
psize_t i, pmr_count;
struct vm_page *firstpg = pg;
for (i = 0; i < count; i++) {
KASSERT(pg->uobject == NULL);
KASSERT(pg->uanon == NULL);
KASSERT(atop(VM_PAGE_TO_PHYS(&pg[i])) ==
atop(VM_PAGE_TO_PHYS(pg)) + i);
if (!((pg[i].pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg[i].pg_flags))) {
printf("Flags: 0x%x, will panic now.\n",
pg[i].pg_flags);
}
KASSERT((pg[i].pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg[i].pg_flags));
atomic_setbits_int(&pg[i].pg_flags, PQ_FREE);
atomic_clearbits_int(&pg[i].pg_flags, PG_ZERO);
}
uvm_lock_fpageq();
for (i = count; i > 0; i -= pmr_count) {
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(pg)));
KASSERT(pmr != NULL);
pmr_count = MIN(i, pmr->high - atop(VM_PAGE_TO_PHYS(pg)));
pg->fpgsz = pmr_count;
uvm_pmr_insert(pmr, pg, 0);
atomic_add_int(&uvmexp.free, pmr_count);
pg += pmr_count;
}
wakeup(&uvmexp.free);
if (atomic_load_sint(&uvmexp.zeropages) < UVM_PAGEZERO_TARGET)
wakeup(&uvmexp.zeropages);
uvm_wakeup_pla(VM_PAGE_TO_PHYS(firstpg), ptoa(count));
uvm_unlock_fpageq();
}
void
uvm_pmr_freepageq(struct pglist *pgl)
{
struct vm_page *pg;
paddr_t pstart;
psize_t plen;
TAILQ_FOREACH(pg, pgl, pageq) {
KASSERT(pg->uobject == NULL);
KASSERT(pg->uanon == NULL);
if (!((pg->pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg->pg_flags))) {
printf("Flags: 0x%x, will panic now.\n",
pg->pg_flags);
}
KASSERT((pg->pg_flags & PQ_FREE) == 0 &&
VALID_FLAGS(pg->pg_flags));
atomic_setbits_int(&pg->pg_flags, PQ_FREE);
atomic_clearbits_int(&pg->pg_flags, PG_ZERO);
}
uvm_lock_fpageq();
while (!TAILQ_EMPTY(pgl)) {
pg = TAILQ_FIRST(pgl);
if (pg == TAILQ_NEXT(pg, pageq) + 1) {
plen = uvm_pmr_remove_1strange_reverse(pgl, &pstart);
} else {
pstart = VM_PAGE_TO_PHYS(TAILQ_FIRST(pgl));
plen = uvm_pmr_remove_1strange(pgl, 0, NULL, 0);
}
atomic_add_int(&uvmexp.free, plen);
uvm_wakeup_pla(pstart, ptoa(plen));
}
wakeup(&uvmexp.free);
if (atomic_load_sint(&uvmexp.zeropages) < UVM_PAGEZERO_TARGET)
wakeup(&uvmexp.zeropages);
uvm_unlock_fpageq();
return;
}
struct uvm_pmemrange *
uvm_pmemrange_use_insert(struct uvm_pmemrange_use *useq,
struct uvm_pmemrange *pmr)
{
struct uvm_pmemrange *iter;
int cmp = 1;
TAILQ_FOREACH(iter, useq, pmr_use) {
cmp = uvm_pmemrange_use_cmp(pmr, iter);
if (cmp == 0)
return iter;
if (cmp == -1)
break;
}
if (iter == NULL)
TAILQ_INSERT_TAIL(useq, pmr, pmr_use);
else
TAILQ_INSERT_BEFORE(iter, pmr, pmr_use);
return NULL;
}
#ifdef DEBUG
void
uvm_pmr_assertvalid(struct uvm_pmemrange *pmr)
{
struct vm_page *prev, *next, *i, *xref;
int lcv, mti;
if (pmr->nsegs == 0)
return;
RBT_FOREACH(i, uvm_pmr_addr, &pmr->addr) {
KASSERT(i->fpgsz > 0);
KASSERT(atop(VM_PAGE_TO_PHYS(i)) >= pmr->low);
KASSERT(atop(VM_PAGE_TO_PHYS(i)) + i->fpgsz
<= pmr->high);
for (lcv = 0; lcv < i->fpgsz; lcv++) {
KASSERT(lcv == 0 || i[lcv].fpgsz == 0);
KASSERT(VALID_FLAGS(i[lcv].pg_flags) &&
(i[lcv].pg_flags & PQ_FREE) == PQ_FREE);
KASSERT(i[lcv].wire_count == 0);
KASSERT(i[lcv].uanon == (void*)0xdeadbeef ||
i[lcv].uanon == NULL);
KASSERT(i[lcv].uobject == (void*)0xdeadbeef ||
i[lcv].uobject == NULL);
KASSERT(uvm_pmr_pg_to_memtype(&i[0]) ==
uvm_pmr_pg_to_memtype(&i[lcv]));
}
prev = RBT_PREV(uvm_pmr_addr, i);
if (prev != NULL) {
KASSERT(uvm_pmr_pg_to_memtype(i) !=
uvm_pmr_pg_to_memtype(prev) ||
atop(VM_PAGE_TO_PHYS(i)) >
atop(VM_PAGE_TO_PHYS(prev)) + prev->fpgsz ||
prev + prev->fpgsz != i);
}
if (i->fpgsz == 1) {
TAILQ_FOREACH(xref,
&pmr->single[uvm_pmr_pg_to_memtype(i)], pageq) {
if (xref == i)
break;
}
KASSERT(xref == i);
} else {
KASSERT(RBT_FIND(uvm_pmr_size,
&pmr->size[uvm_pmr_pg_to_memtype(i)], i + 1) ==
i + 1);
}
}
for (mti = 0; mti < UVM_PMR_MEMTYPE_MAX; mti++) {
for (i = uvm_pmr_nfindsz(pmr, 1, mti); i != NULL; i = next) {
next = uvm_pmr_nextsz(pmr, i, mti);
if (next != NULL) {
KASSERT(i->fpgsz <=
next->fpgsz);
}
KASSERT(RBT_FIND(uvm_pmr_addr, &pmr->addr, i) == i);
KASSERT(uvm_pmr_pg_to_memtype(i) == mti);
}
}
lcv = 0;
RBT_FOREACH(i, uvm_pmr_addr, &pmr->addr)
lcv++;
KASSERT(pmr->nsegs == lcv);
}
#endif
void
uvm_pmr_split(paddr_t pageno)
{
struct uvm_pmemrange *pmr, *drain;
struct vm_page *rebuild, *prev, *next;
psize_t prev_sz;
uvm_lock_fpageq();
pmr = uvm_pmemrange_find(pageno);
if (pmr == NULL || !(pmr->low < pageno)) {
uvm_unlock_fpageq();
return;
}
KASSERT(pmr->low < pageno);
KASSERT(pmr->high > pageno);
uvm_unlock_fpageq();
drain = uvm_pmr_allocpmr();
uvm_lock_fpageq();
pmr = uvm_pmemrange_find(pageno);
if (pmr == NULL || !(pmr->low < pageno)) {
printf("uvm_pmr_split: lost one pmr\n");
uvm_unlock_fpageq();
return;
}
drain->low = pageno;
drain->high = pmr->high;
drain->use = pmr->use;
uvm_pmr_assertvalid(pmr);
uvm_pmr_assertvalid(drain);
KASSERT(drain->nsegs == 0);
RBT_FOREACH(rebuild, uvm_pmr_addr, &pmr->addr) {
if (atop(VM_PAGE_TO_PHYS(rebuild)) >= pageno)
break;
}
if (rebuild == NULL)
prev = RBT_MAX(uvm_pmr_addr, &pmr->addr);
else
prev = RBT_PREV(uvm_pmr_addr, rebuild);
KASSERT(prev == NULL || atop(VM_PAGE_TO_PHYS(prev)) < pageno);
if (prev != NULL &&
atop(VM_PAGE_TO_PHYS(prev)) + prev->fpgsz > pageno) {
psize_t before, after;
KASSERT(atop(VM_PAGE_TO_PHYS(prev)) < pageno);
uvm_pmr_remove(pmr, prev);
prev_sz = prev->fpgsz;
before = pageno - atop(VM_PAGE_TO_PHYS(prev));
after = atop(VM_PAGE_TO_PHYS(prev)) + prev_sz - pageno;
KASSERT(before > 0);
KASSERT(after > 0);
prev->fpgsz = before;
uvm_pmr_insert(pmr, prev, 1);
(prev + before)->fpgsz = after;
uvm_pmr_insert(drain, prev + before, 1);
}
for (; rebuild != NULL; rebuild = next) {
next = RBT_NEXT(uvm_pmr_addr, rebuild);
uvm_pmr_remove(pmr, rebuild);
uvm_pmr_insert(drain, rebuild, 1);
}
pmr->high = pageno;
uvm_pmr_assertvalid(pmr);
uvm_pmr_assertvalid(drain);
RBT_INSERT(uvm_pmemrange_addr, &uvm.pmr_control.addr, drain);
uvm_pmemrange_use_insert(&uvm.pmr_control.use, drain);
uvm_unlock_fpageq();
}
void
uvm_pmr_use_inc(paddr_t low, paddr_t high)
{
struct uvm_pmemrange *pmr;
paddr_t sz;
high++;
high = atop(trunc_page(high));
low = atop(round_page(low));
uvm_pmr_split(low);
uvm_pmr_split(high);
sz = 0;
uvm_lock_fpageq();
RBT_FOREACH(pmr, uvm_pmemrange_addr, &uvm.pmr_control.addr) {
if (PMR_IS_SUBRANGE_OF(pmr->low, pmr->high, low, high)) {
TAILQ_REMOVE(&uvm.pmr_control.use, pmr, pmr_use);
pmr->use++;
sz += pmr->high - pmr->low;
uvm_pmemrange_use_insert(&uvm.pmr_control.use, pmr);
}
uvm_pmr_assertvalid(pmr);
}
uvm_unlock_fpageq();
KASSERT(sz >= high - low);
}
struct uvm_pmemrange *
uvm_pmr_allocpmr(void)
{
struct uvm_pmemrange *nw;
int i;
if (!uvm.page_init_done) {
nw = (struct uvm_pmemrange *)
uvm_pageboot_alloc(sizeof(struct uvm_pmemrange));
} else {
nw = malloc(sizeof(struct uvm_pmemrange),
M_VMMAP, M_NOWAIT);
}
KASSERT(nw != NULL);
memset(nw, 0, sizeof(struct uvm_pmemrange));
RBT_INIT(uvm_pmr_addr, &nw->addr);
for (i = 0; i < UVM_PMR_MEMTYPE_MAX; i++) {
RBT_INIT(uvm_pmr_size, &nw->size[i]);
TAILQ_INIT(&nw->single[i]);
}
return nw;
}
void
uvm_pmr_init(void)
{
struct uvm_pmemrange *new_pmr;
int i;
TAILQ_INIT(&uvm.pmr_control.use);
RBT_INIT(uvm_pmemrange_addr, &uvm.pmr_control.addr);
TAILQ_INIT(&uvm.pmr_control.allocs);
new_pmr = uvm_pmr_allocpmr();
new_pmr->low = 0;
new_pmr->high = atop((paddr_t)-1) + 1;
RBT_INSERT(uvm_pmemrange_addr, &uvm.pmr_control.addr, new_pmr);
uvm_pmemrange_use_insert(&uvm.pmr_control.use, new_pmr);
for (i = 0; uvm_md_constraints[i] != NULL; i++) {
uvm_pmr_use_inc(uvm_md_constraints[i]->ucr_low,
uvm_md_constraints[i]->ucr_high);
}
}
struct uvm_pmemrange *
uvm_pmemrange_find(paddr_t pageno)
{
struct uvm_pmemrange *pmr;
pmr = RBT_ROOT(uvm_pmemrange_addr, &uvm.pmr_control.addr);
while (pmr != NULL) {
if (pmr->low > pageno)
pmr = RBT_LEFT(uvm_pmemrange_addr, pmr);
else if (pmr->high <= pageno)
pmr = RBT_RIGHT(uvm_pmemrange_addr, pmr);
else
break;
}
return pmr;
}
#if defined(DDB) || defined(DEBUG)
int
uvm_pmr_isfree(struct vm_page *pg)
{
struct vm_page *r;
struct uvm_pmemrange *pmr;
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(pg)));
if (pmr == NULL)
return 0;
r = RBT_NFIND(uvm_pmr_addr, &pmr->addr, pg);
if (r == NULL)
r = RBT_MAX(uvm_pmr_addr, &pmr->addr);
else if (r != pg)
r = RBT_PREV(uvm_pmr_addr, r);
if (r == NULL)
return 0;
KDASSERT(atop(VM_PAGE_TO_PHYS(r)) <= atop(VM_PAGE_TO_PHYS(pg)));
return atop(VM_PAGE_TO_PHYS(r)) + r->fpgsz >
atop(VM_PAGE_TO_PHYS(pg));
}
#endif
struct vm_page*
uvm_pmr_rootupdate(struct uvm_pmemrange *pmr, struct vm_page *init_root,
paddr_t start, paddr_t end, int memtype)
{
int direction;
struct vm_page *root;
struct vm_page *high, *high_next;
struct vm_page *low, *low_next;
KDASSERT(pmr != NULL && init_root != NULL);
root = init_root;
if (start != 0 && atop(VM_PAGE_TO_PHYS(root)) + root->fpgsz <= start)
direction = 1;
else if (end != 0 && atop(VM_PAGE_TO_PHYS(root)) >= end)
direction = -1;
else
return root;
while (root && !PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(root)),
atop(VM_PAGE_TO_PHYS(root)) + root->fpgsz,
start, end)) {
if (direction == 1)
root = RBT_RIGHT(uvm_objtree, root);
else
root = RBT_LEFT(uvm_objtree, root);
}
if (root == NULL || uvm_pmr_pg_to_memtype(root) == memtype)
return root;
high = root;
high_next = RBT_RIGHT(uvm_objtree, high);
while (high_next != NULL && PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(high_next)),
atop(VM_PAGE_TO_PHYS(high_next)) + high_next->fpgsz,
start, end)) {
high = high_next;
if (uvm_pmr_pg_to_memtype(high) == memtype)
return high;
high_next = RBT_RIGHT(uvm_objtree, high);
}
low = root;
low_next = RBT_LEFT(uvm_objtree, low);
while (low_next != NULL && PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(low_next)),
atop(VM_PAGE_TO_PHYS(low_next)) + low_next->fpgsz,
start, end)) {
low = low_next;
if (uvm_pmr_pg_to_memtype(low) == memtype)
return low;
low_next = RBT_LEFT(uvm_objtree, low);
}
if (low == high)
return NULL;
for (low = RBT_NEXT(uvm_pmr_addr, low);
low != high;
low = RBT_NEXT(uvm_pmr_addr, low)) {
KDASSERT(PMR_IS_SUBRANGE_OF(atop(VM_PAGE_TO_PHYS(low)),
atop(VM_PAGE_TO_PHYS(low)) + low->fpgsz,
start, end));
if (uvm_pmr_pg_to_memtype(low) == memtype)
return low;
}
return NULL;
}
psize_t
uvm_pmr_get1page(psize_t count, int memtype_init, struct pglist *result,
paddr_t start, paddr_t end, int memtype_only)
{
struct uvm_pmemrange *pmr;
struct vm_page *found, *splitpg;
psize_t fcount;
int memtype;
fcount = 0;
TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) {
if (fcount == count)
break;
if (!(start == 0 && end == 0) &&
!PMR_INTERSECTS_WITH(pmr->low, pmr->high, start, end))
continue;
if (pmr->nsegs == 0)
continue;
memtype = memtype_init;
while (fcount != count) {
found = TAILQ_FIRST(&pmr->single[memtype]);
while (found && !PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(found)),
atop(VM_PAGE_TO_PHYS(found)) + 1,
start, end))
found = TAILQ_NEXT(found, pageq);
if (found == NULL) {
found = RBT_MIN(uvm_pmr_size,
&pmr->size[memtype]);
if (found != NULL) {
found--;
if (!PMR_INTERSECTS_WITH(
atop(VM_PAGE_TO_PHYS(found)),
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz, start, end))
found = NULL;
}
}
if (found == NULL) {
found = RBT_ROOT(uvm_pmr_addr, &pmr->addr);
if (found != NULL) {
found = uvm_pmr_rootupdate(pmr, found,
start, end, memtype);
}
}
if (found != NULL) {
uvm_pmr_assertvalid(pmr);
uvm_pmr_remove_size(pmr, found);
if (end != 0 && atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz > end) {
psize_t splitsz =
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz - end;
uvm_pmr_remove_addr(pmr, found);
uvm_pmr_assertvalid(pmr);
found->fpgsz -= splitsz;
splitpg = found + found->fpgsz;
splitpg->fpgsz = splitsz;
uvm_pmr_insert(pmr, splitpg, 1);
KASSERT(start == 0 ||
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz > start);
uvm_pmr_insert_addr(pmr, found, 1);
}
while (found->fpgsz > 0 && fcount < count &&
(start == 0 ||
atop(VM_PAGE_TO_PHYS(found)) +
found->fpgsz > start)) {
found->fpgsz--;
fcount++;
TAILQ_INSERT_HEAD(result,
&found[found->fpgsz], pageq);
}
if (found->fpgsz > 0) {
uvm_pmr_insert_size(pmr, found);
KDASSERT(fcount == count);
uvm_pmr_assertvalid(pmr);
return fcount;
}
uvm_pmr_remove_addr(pmr, found);
uvm_pmr_assertvalid(pmr);
} else {
if (memtype_only)
break;
memtype += 1;
if (memtype == UVM_PMR_MEMTYPE_MAX)
memtype = 0;
if (memtype == memtype_init)
break;
}
}
}
return fcount;
}
#ifdef DDB
void
uvm_pmr_print(void)
{
struct uvm_pmemrange *pmr;
struct vm_page *pg;
psize_t size[UVM_PMR_MEMTYPE_MAX];
psize_t free;
int useq_len;
int mt;
printf("Ranges, use queue:\n");
useq_len = 0;
TAILQ_FOREACH(pmr, &uvm.pmr_control.use, pmr_use) {
useq_len++;
free = 0;
for (mt = 0; mt < UVM_PMR_MEMTYPE_MAX; mt++) {
pg = RBT_MAX(uvm_pmr_size, &pmr->size[mt]);
if (pg != NULL)
pg--;
else
pg = TAILQ_FIRST(&pmr->single[mt]);
size[mt] = (pg == NULL ? 0 : pg->fpgsz);
RBT_FOREACH(pg, uvm_pmr_addr, &pmr->addr)
free += pg->fpgsz;
}
printf("* [0x%lx-0x%lx] use=%d nsegs=%ld",
(unsigned long)pmr->low, (unsigned long)pmr->high,
pmr->use, (unsigned long)pmr->nsegs);
for (mt = 0; mt < UVM_PMR_MEMTYPE_MAX; mt++) {
printf(" maxsegsz[%d]=0x%lx", mt,
(unsigned long)size[mt]);
}
printf(" free=0x%lx\n", (unsigned long)free);
}
printf("#ranges = %d\n", useq_len);
}
#endif
int
uvm_wait_pla(paddr_t low, paddr_t high, paddr_t size, int failok)
{
struct uvm_pmalloc pma;
const char *wmsg = "pmrwait";
KASSERT(curcpu()->ci_idepth == 0);
if (curproc == uvm.pagedaemon_proc) {
uvm_unlock_fpageq();
if (bufbackoff(NULL, atop(size)) >= atop(size)) {
uvm_lock_fpageq();
return 0;
}
uvm_lock_fpageq();
printf("pagedaemon: wait_pla deadlock detected!\n");
msleep_nsec(&uvmexp.free, &uvm.fpageqlock, PVM, wmsg,
MSEC_TO_NSEC(125));
#if defined(DEBUG)
panic("wait_pla pagedaemon deadlock");
#endif
return 0;
}
for (;;) {
pma.pm_constraint.ucr_low = low;
pma.pm_constraint.ucr_high = high;
pma.pm_size = size;
pma.pm_flags = UVM_PMA_LINKED;
TAILQ_INSERT_TAIL(&uvm.pmr_control.allocs, &pma, pmq);
wakeup(&uvm.pagedaemon);
while (pma.pm_flags & UVM_PMA_LINKED)
msleep_nsec(&pma, &uvm.fpageqlock, PVM, wmsg, INFSLP);
if (!(pma.pm_flags & UVM_PMA_FREED) &&
pma.pm_flags & UVM_PMA_FAIL) {
if (failok)
return ENOMEM;
printf("uvm_wait: failed to free %ld pages between "
"0x%lx-0x%lx\n", atop(size), low, high);
} else
return 0;
}
}
void
uvm_wakeup_pla(paddr_t low, psize_t len)
{
struct uvm_pmalloc *pma, *pma_next;
paddr_t high;
high = low + len;
for (pma = TAILQ_FIRST(&uvm.pmr_control.allocs); pma != NULL;
pma = pma_next) {
pma_next = TAILQ_NEXT(pma, pmq);
if (low < pma->pm_constraint.ucr_high &&
high > pma->pm_constraint.ucr_low) {
pma->pm_flags |= UVM_PMA_FREED;
pma->pm_flags &= ~UVM_PMA_LINKED;
TAILQ_REMOVE(&uvm.pmr_control.allocs, pma, pmq);
wakeup(pma);
}
}
}
void
uvm_pagezero_thread(void *arg)
{
struct pglist pgl;
struct vm_page *pg;
int count;
curproc->p_p->ps_nice = NZERO + PRIO_MAX;
KERNEL_UNLOCK();
TAILQ_INIT(&pgl);
for (;;) {
uvm_lock_fpageq();
while (atomic_load_sint(&uvmexp.zeropages) >= UVM_PAGEZERO_TARGET ||
(count = uvm_pmr_get1page(16, UVM_PMR_MEMTYPE_DIRTY,
&pgl, 0, 0, 1)) == 0) {
msleep_nsec(&uvmexp.zeropages, &uvm.fpageqlock,
MAXPRI, "pgzero", INFSLP);
}
uvm_unlock_fpageq();
TAILQ_FOREACH(pg, &pgl, pageq) {
uvm_pagezero(pg);
atomic_setbits_int(&pg->pg_flags, PG_ZERO);
}
uvm_lock_fpageq();
while (!TAILQ_EMPTY(&pgl))
uvm_pmr_remove_1strange(&pgl, 0, NULL, 0);
atomic_add_int(&uvmexp.zeropages, count);
uvm_unlock_fpageq();
yield();
}
}
#if defined(MULTIPROCESSOR) && defined(__HAVE_UVM_PERCPU)
int
uvm_pmr_cache_alloc(struct uvm_pmr_cache_item *upci)
{
struct vm_page *pg;
struct pglist pgl;
int flags = UVM_PLA_NOWAIT|UVM_PLA_NOWAKE;
int npages = UVM_PMR_CACHEMAGSZ;
splassert(IPL_BIO);
KASSERT(upci->upci_npages == 0);
TAILQ_INIT(&pgl);
if (uvm_pmr_getpages(npages, 0, 0, 1, 0, npages, flags, &pgl))
return -1;
while ((pg = TAILQ_FIRST(&pgl)) != NULL) {
TAILQ_REMOVE(&pgl, pg, pageq);
upci->upci_pages[upci->upci_npages] = pg;
upci->upci_npages++;
}
atomic_add_int(&uvmexp.percpucaches, npages);
return 0;
}
struct vm_page *
uvm_pmr_cache_get(int flags)
{
struct uvm_pmr_cache *upc = &curcpu()->ci_uvm;
struct uvm_pmr_cache_item *upci;
struct vm_page *pg;
int s;
s = splbio();
upci = &upc->upc_magz[upc->upc_actv];
if (upci->upci_npages == 0) {
unsigned int prev;
prev = (upc->upc_actv == 0) ? 1 : 0;
upci = &upc->upc_magz[prev];
if (upci->upci_npages == 0) {
atomic_inc_int(&uvmexp.pcpmiss);
if (uvm_pmr_cache_alloc(upci)) {
splx(s);
return uvm_pmr_getone(flags);
}
}
upc->upc_actv = prev;
} else {
atomic_inc_int(&uvmexp.pcphit);
}
atomic_dec_int(&uvmexp.percpucaches);
upci->upci_npages--;
pg = upci->upci_pages[upci->upci_npages];
splx(s);
if (flags & UVM_PLA_ZERO)
uvm_pagezero(pg);
return pg;
}
unsigned int
uvm_pmr_cache_free(struct uvm_pmr_cache_item *upci)
{
struct pglist pgl;
unsigned int i;
splassert(IPL_BIO);
TAILQ_INIT(&pgl);
for (i = 0; i < upci->upci_npages; i++)
TAILQ_INSERT_TAIL(&pgl, upci->upci_pages[i], pageq);
uvm_pmr_freepageq(&pgl);
atomic_sub_int(&uvmexp.percpucaches, upci->upci_npages);
upci->upci_npages = 0;
memset(upci->upci_pages, 0, sizeof(upci->upci_pages));
return i;
}
void
uvm_pmr_cache_put(struct vm_page *pg)
{
struct uvm_pmr_cache *upc = &curcpu()->ci_uvm;
struct uvm_pmr_cache_item *upci;
struct uvm_pmemrange *pmr;
int s;
pmr = uvm_pmemrange_find(atop(VM_PAGE_TO_PHYS(pg)));
if (pmr->use > 0) {
uvm_pmr_freepages(pg, 1);
return;
}
KASSERT(pg->wire_count == 0);
KASSERT(pg->uanon == (void*)0xdeadbeef || pg->uanon == NULL);
KASSERT(pg->uobject == (void*)0xdeadbeef || pg->uobject == NULL);
s = splbio();
upci = &upc->upc_magz[upc->upc_actv];
if (upci->upci_npages >= UVM_PMR_CACHEMAGSZ) {
unsigned int prev;
prev = (upc->upc_actv == 0) ? 1 : 0;
upci = &upc->upc_magz[prev];
if (upci->upci_npages > 0)
uvm_pmr_cache_free(upci);
upc->upc_actv = prev;
KASSERT(upci->upci_npages == 0);
}
upci->upci_pages[upci->upci_npages] = pg;
upci->upci_npages++;
atomic_inc_int(&uvmexp.percpucaches);
splx(s);
}
unsigned int
uvm_pmr_cache_drain(void)
{
struct uvm_pmr_cache *upc = &curcpu()->ci_uvm;
unsigned int freed = 0;
int s;
s = splbio();
freed += uvm_pmr_cache_free(&upc->upc_magz[0]);
freed += uvm_pmr_cache_free(&upc->upc_magz[1]);
splx(s);
return freed;
}
#else
struct vm_page *
uvm_pmr_cache_get(int flags)
{
return uvm_pmr_getone(flags);
}
void
uvm_pmr_cache_put(struct vm_page *pg)
{
uvm_pmr_freepages(pg, 1);
}
unsigned int
uvm_pmr_cache_drain(void)
{
return 0;
}
#endif