#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sched.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/smr.h>
#include <uvm/uvm.h>
RBT_GENERATE(uvm_objtree, vm_page, objt, uvm_pagecmp);
int
uvm_pagecmp(const struct vm_page *a, const struct vm_page *b)
{
return a->offset < b->offset ? -1 : a->offset > b->offset;
}
struct vm_physseg vm_physmem[VM_PHYSSEG_MAX];
int vm_nphysseg = 0;
static vaddr_t virtual_space_start;
static vaddr_t virtual_space_end;
static void uvm_pageinsert(struct vm_page *);
static void uvm_pageremove(struct vm_page *);
int uvm_page_owner_locked_p(struct vm_page *, boolean_t);
static inline void
uvm_pageinsert(struct vm_page *pg)
{
struct vm_page *dupe;
KASSERT(UVM_OBJ_IS_DUMMY(pg->uobject) ||
rw_write_held(pg->uobject->vmobjlock));
KASSERT((pg->pg_flags & PG_TABLED) == 0);
dupe = RBT_INSERT(uvm_objtree, &pg->uobject->memt, pg);
KASSERT(dupe == NULL);
atomic_setbits_int(&pg->pg_flags, PG_TABLED);
pg->uobject->uo_npages++;
}
static inline void
uvm_pageremove(struct vm_page *pg)
{
KASSERT(UVM_OBJ_IS_DUMMY(pg->uobject) ||
rw_write_held(pg->uobject->vmobjlock));
KASSERT(pg->pg_flags & PG_TABLED);
RBT_REMOVE(uvm_objtree, &pg->uobject->memt, pg);
atomic_clearbits_int(&pg->pg_flags, PG_TABLED);
pg->uobject->uo_npages--;
pg->uobject = NULL;
pg->pg_version++;
}
void
uvm_page_init(vaddr_t *kvm_startp, vaddr_t *kvm_endp)
{
vsize_t freepages, pagecount, n;
vm_page_t pagearray, curpg;
int lcv, i;
paddr_t paddr, pgno;
struct vm_physseg *seg;
TAILQ_INIT(&uvm.page_active);
TAILQ_INIT(&uvm.page_inactive);
mtx_init(&uvm.pageqlock, IPL_VM);
mtx_init(&uvm.fpageqlock, IPL_VM);
uvm_pmr_init();
if (vm_nphysseg == 0)
panic("uvm_page_bootstrap: no memory pre-allocated");
freepages = 0;
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
freepages += (seg->end - seg->start);
pagecount = (((paddr_t)freepages + 1) << PAGE_SHIFT) /
(PAGE_SIZE + sizeof(struct vm_page));
pagearray = (vm_page_t)uvm_pageboot_alloc(pagecount *
sizeof(struct vm_page));
memset(pagearray, 0, pagecount * sizeof(struct vm_page));
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++) {
n = seg->end - seg->start;
if (n > pagecount) {
panic("uvm_page_init: lost %ld page(s) in init",
(long)(n - pagecount));
}
seg->pgs = pagearray;
pagearray += n;
pagecount -= n;
seg->lastpg = seg->pgs + (n - 1);
pgno = seg->start;
paddr = ptoa(pgno);
for (i = 0, curpg = seg->pgs; i < n;
i++, curpg++, pgno++, paddr += PAGE_SIZE) {
curpg->phys_addr = paddr;
VM_MDPAGE_INIT(curpg);
curpg->uobject = NULL;
curpg->uanon = NULL;
if (pgno >= seg->avail_start &&
pgno < seg->avail_end) {
uvmexp.npages++;
}
}
uvm_pmr_freepages(&seg->pgs[seg->avail_start - seg->start],
seg->avail_end - seg->avail_start);
}
*kvm_startp = round_page(virtual_space_start);
*kvm_endp = trunc_page(virtual_space_end);
mtx_init(&uvm.aiodoned_lock, IPL_BIO);
uvmexp.reserve_pagedaemon = 32;
uvmexp.reserve_kernel = uvmexp.reserve_pagedaemon + 32;
uvm.page_init_done = TRUE;
}
void
uvm_setpagesize(void)
{
if (uvmexp.pagesize == 0)
uvmexp.pagesize = DEFAULT_PAGE_SIZE;
uvmexp.pagemask = uvmexp.pagesize - 1;
if ((uvmexp.pagemask & uvmexp.pagesize) != 0)
panic("uvm_setpagesize: page size not a power of two");
for (uvmexp.pageshift = 0; ; uvmexp.pageshift++)
if ((1 << uvmexp.pageshift) == uvmexp.pagesize)
break;
}
vaddr_t
uvm_pageboot_alloc(vsize_t size)
{
#if defined(PMAP_STEAL_MEMORY)
vaddr_t addr;
addr = pmap_steal_memory(size, &virtual_space_start,
&virtual_space_end);
return addr;
#else
static boolean_t initialized = FALSE;
vaddr_t addr, vaddr;
paddr_t paddr;
size = round_page(size);
if (initialized == FALSE) {
pmap_virtual_space(&virtual_space_start, &virtual_space_end);
virtual_space_start = round_page(virtual_space_start);
virtual_space_end = trunc_page(virtual_space_end);
initialized = TRUE;
}
if (virtual_space_start == virtual_space_end ||
(virtual_space_end - virtual_space_start) < size)
panic("uvm_pageboot_alloc: out of virtual space");
addr = virtual_space_start;
#ifdef PMAP_GROWKERNEL
if (uvm_maxkaddr < (addr + size)) {
uvm_maxkaddr = pmap_growkernel(addr + size);
if (uvm_maxkaddr < (addr + size))
panic("uvm_pageboot_alloc: pmap_growkernel() failed");
}
#endif
virtual_space_start += size;
for (vaddr = round_page(addr) ; vaddr < addr + size ;
vaddr += PAGE_SIZE) {
if (!uvm_page_physget(&paddr))
panic("uvm_pageboot_alloc: out of memory");
pmap_kenter_pa(vaddr, paddr, PROT_READ | PROT_WRITE);
}
pmap_update(pmap_kernel());
return addr;
#endif
}
#if !defined(PMAP_STEAL_MEMORY)
boolean_t
uvm_page_physget(paddr_t *paddrp)
{
int lcv;
struct vm_physseg *seg;
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST) || \
(VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
for (lcv = vm_nphysseg - 1, seg = vm_physmem + lcv; lcv >= 0;
lcv--, seg--)
#else
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
#endif
{
if (uvm.page_init_done == TRUE)
panic("uvm_page_physget: called _after_ bootstrap");
if (seg->avail_start == seg->start &&
seg->avail_start < seg->avail_end) {
*paddrp = ptoa(seg->avail_start);
seg->avail_start++;
seg->start++;
if (seg->avail_start == seg->end) {
if (vm_nphysseg == 1)
panic("uvm_page_physget: out of memory!");
vm_nphysseg--;
for (; lcv < vm_nphysseg; lcv++, seg++)
seg[0] = seg[1];
}
return TRUE;
}
if (seg->avail_end == seg->end &&
seg->avail_start < seg->avail_end) {
*paddrp = ptoa(seg->avail_end - 1);
seg->avail_end--;
seg->end--;
if (seg->avail_end == seg->start) {
if (vm_nphysseg == 1)
panic("uvm_page_physget: out of memory!");
vm_nphysseg--;
for (; lcv < vm_nphysseg ; lcv++, seg++)
seg[0] = seg[1];
}
return TRUE;
}
}
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST) || \
(VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
for (lcv = vm_nphysseg - 1, seg = vm_physmem + lcv; lcv >= 0;
lcv--, seg--)
#else
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
#endif
{
if (seg->avail_start >= seg->avail_end)
continue;
*paddrp = ptoa(seg->avail_start);
seg->avail_start++;
seg->start = seg->avail_start;
if (seg->avail_start == seg->end) {
if (vm_nphysseg == 1)
panic("uvm_page_physget: out of memory!");
vm_nphysseg--;
for (; lcv < vm_nphysseg ; lcv++, seg++)
seg[0] = seg[1];
}
return TRUE;
}
return FALSE;
}
#endif
void
uvm_page_physload(paddr_t start, paddr_t end, paddr_t avail_start,
paddr_t avail_end, int flags)
{
int preload, lcv;
psize_t npages;
struct vm_page *pgs;
struct vm_physseg *ps, *seg;
#ifdef DIAGNOSTIC
if (uvmexp.pagesize == 0)
panic("uvm_page_physload: page size not set!");
if (start >= end)
panic("uvm_page_physload: start >= end");
#endif
if (vm_nphysseg == VM_PHYSSEG_MAX) {
printf("uvm_page_physload: unable to load physical memory "
"segment\n");
printf("\t%d segments allocated, ignoring 0x%llx -> 0x%llx\n",
VM_PHYSSEG_MAX, (long long)start, (long long)end);
printf("\tincrease VM_PHYSSEG_MAX\n");
return;
}
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg; lcv++, seg++) {
if (seg->pgs)
break;
}
preload = (lcv == vm_nphysseg);
if (!preload) {
paddr_t paddr;
npages = end - start;
pgs = km_alloc(round_page(npages * sizeof(*pgs)),
&kv_any, &kp_zero, &kd_waitok);
if (pgs == NULL) {
printf("uvm_page_physload: can not malloc vm_page "
"structs for segment\n");
printf("\tignoring 0x%lx -> 0x%lx\n", start, end);
return;
}
for (lcv = 0, paddr = ptoa(start); lcv < npages;
lcv++, paddr += PAGE_SIZE) {
pgs[lcv].phys_addr = paddr;
VM_MDPAGE_INIT(&pgs[lcv]);
pgs[lcv].uobject = NULL;
pgs[lcv].uanon = NULL;
if (atop(paddr) >= avail_start &&
atop(paddr) < avail_end) {
if (flags & PHYSLOAD_DEVICE) {
atomic_setbits_int(&pgs[lcv].pg_flags,
PG_DEV);
pgs[lcv].wire_count = 1;
} else {
#if defined(VM_PHYSSEG_NOADD)
panic("uvm_page_physload: tried to add RAM after vm_mem_init");
#endif
}
}
}
if ((flags & PHYSLOAD_DEVICE) == 0) {
uvm_pmr_freepages(&pgs[avail_start - start],
avail_end - avail_start);
}
} else {
pgs = NULL;
npages = 0;
}
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_RANDOM)
ps = &vm_physmem[vm_nphysseg];
#elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
{
int x;
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg; lcv++, seg++)
if (start < seg->start)
break;
ps = seg;
for (x = vm_nphysseg, seg = vm_physmem + x - 1; x > lcv;
x--, seg--)
seg[1] = seg[0];
}
#elif (VM_PHYSSEG_STRAT == VM_PSTRAT_BIGFIRST)
{
int x;
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg; lcv++, seg++)
if ((end - start) >
(seg->end - seg->start))
break;
ps = &vm_physmem[lcv];
for (x = vm_nphysseg, seg = vm_physmem + x - 1; x > lcv;
x--, seg--)
seg[1] = seg[0];
}
#else
panic("uvm_page_physload: unknown physseg strategy selected!");
#endif
ps->start = start;
ps->end = end;
ps->avail_start = avail_start;
ps->avail_end = avail_end;
if (preload) {
ps->pgs = NULL;
} else {
ps->pgs = pgs;
ps->lastpg = pgs + npages - 1;
}
vm_nphysseg++;
return;
}
#ifdef DDB
void uvm_page_physdump(void);
void
uvm_page_physdump(void)
{
int lcv;
struct vm_physseg *seg;
printf("uvm_page_physdump: physical memory config [segs=%d of %d]:\n",
vm_nphysseg, VM_PHYSSEG_MAX);
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++)
printf("0x%llx->0x%llx [0x%llx->0x%llx]\n",
(long long)seg->start,
(long long)seg->end,
(long long)seg->avail_start,
(long long)seg->avail_end);
printf("STRATEGY = ");
switch (VM_PHYSSEG_STRAT) {
case VM_PSTRAT_RANDOM: printf("RANDOM\n"); break;
case VM_PSTRAT_BSEARCH: printf("BSEARCH\n"); break;
case VM_PSTRAT_BIGFIRST: printf("BIGFIRST\n"); break;
default: printf("<<UNKNOWN>>!!!!\n");
}
}
#endif
void
uvm_shutdown(void)
{
#ifdef UVM_SWAP_ENCRYPT
uvm_swap_finicrypt_all();
#endif
smr_flush();
}
void
uvm_pagealloc_pg(struct vm_page *pg, struct uvm_object *obj, voff_t off,
struct vm_anon *anon)
{
int flags;
KASSERT(obj == NULL || anon == NULL);
KASSERT(anon == NULL || off == 0);
KASSERT(off == trunc_page(off));
KASSERT(obj == NULL || UVM_OBJ_IS_DUMMY(obj) ||
rw_write_held(obj->vmobjlock));
KASSERT(anon == NULL || anon->an_lock == NULL ||
rw_write_held(anon->an_lock));
flags = PG_BUSY | PG_FAKE;
pg->offset = off;
pg->uobject = obj;
pg->uanon = anon;
KASSERT(uvm_page_owner_locked_p(pg, TRUE));
if (anon) {
anon->an_page = pg;
flags |= PQ_ANON;
} else if (obj)
uvm_pageinsert(pg);
atomic_setbits_int(&pg->pg_flags, flags);
#if defined(UVM_PAGE_TRKOWN)
pg->owner_tag = NULL;
#endif
UVM_PAGE_OWN(pg, "new alloc");
}
int
uvm_pglistalloc(psize_t size, paddr_t low, paddr_t high, paddr_t alignment,
paddr_t boundary, struct pglist *rlist, int nsegs, int flags)
{
KASSERT((alignment & (alignment - 1)) == 0);
KASSERT((boundary & (boundary - 1)) == 0);
KASSERT(!(flags & UVM_PLA_WAITOK) ^ !(flags & UVM_PLA_NOWAIT));
if (size == 0)
return EINVAL;
size = atop(round_page(size));
flags |= UVM_PLA_USERESERVE;
if ((high & PAGE_MASK) != PAGE_MASK) {
printf("uvm_pglistalloc: Upper boundary 0x%lx "
"not on pagemask.\n", (unsigned long)high);
}
if (alignment < PAGE_SIZE)
alignment = PAGE_SIZE;
low = atop(roundup(low, alignment));
high = atop(high + 1);
alignment = atop(alignment);
if (boundary < PAGE_SIZE && boundary != 0)
boundary = PAGE_SIZE;
boundary = atop(boundary);
return uvm_pmr_getpages(size, low, high, alignment, boundary, nsegs,
flags, rlist);
}
void
uvm_pglistfree(struct pglist *list)
{
uvm_pmr_freepageq(list);
}
int
uvm_pagealloc_multi(struct uvm_object *obj, voff_t off, vsize_t size,
int flags)
{
struct pglist plist;
struct vm_page *pg;
int i, r;
KASSERT(UVM_OBJ_IS_BUFCACHE(obj));
KERNEL_ASSERT_LOCKED();
TAILQ_INIT(&plist);
r = uvm_pglistalloc(size, no_constraint.ucr_low,
no_constraint.ucr_high, 0, 0, &plist, atop(round_page(size)),
flags);
if (r == 0) {
i = 0;
while ((pg = TAILQ_FIRST(&plist)) != NULL) {
pg->wire_count = 1;
atomic_setbits_int(&pg->pg_flags, PG_CLEAN | PG_FAKE);
KASSERT((pg->pg_flags & PG_DEV) == 0);
TAILQ_REMOVE(&plist, pg, pageq);
uvm_pagealloc_pg(pg, obj, off + ptoa(i++), NULL);
}
}
return r;
}
struct vm_page *
uvm_pagealloc(struct uvm_object *obj, voff_t off, struct vm_anon *anon,
int flags)
{
struct vm_page *pg = NULL;
int pmr_flags;
KASSERT(obj == NULL || anon == NULL);
KASSERT(anon == NULL || off == 0);
KASSERT(off == trunc_page(off));
KASSERT(obj == NULL || UVM_OBJ_IS_DUMMY(obj) ||
rw_write_held(obj->vmobjlock));
KASSERT(anon == NULL || anon->an_lock == NULL ||
rw_write_held(anon->an_lock));
pmr_flags = UVM_PLA_NOWAIT;
if ((flags & UVM_PGA_USERESERVE) ||
(obj != NULL && UVM_OBJ_IS_KERN_OBJECT(obj)))
pmr_flags |= UVM_PLA_USERESERVE;
if (flags & UVM_PGA_ZERO)
pmr_flags |= UVM_PLA_ZERO;
pg = uvm_pmr_cache_get(pmr_flags);
if (pg == NULL)
return NULL;
uvm_pagealloc_pg(pg, obj, off, anon);
KASSERT((pg->pg_flags & PG_DEV) == 0);
if (flags & UVM_PGA_ZERO)
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
else
atomic_setbits_int(&pg->pg_flags, PG_CLEAN);
return pg;
}
void
uvm_pagerealloc(struct vm_page *pg, struct uvm_object *newobj, voff_t newoff)
{
if (pg->uobject) {
uvm_pageremove(pg);
}
if (newobj) {
pg->uobject = newobj;
pg->offset = newoff;
pg->pg_version++;
uvm_pageinsert(pg);
}
}
void
uvm_pageclean(struct vm_page *pg)
{
u_int flags_to_clear = 0;
#ifdef DEBUG
if (pg->uobject == (void *)0xdeadbeef &&
pg->uanon == (void *)0xdeadbeef) {
panic("uvm_pagefree: freeing free page %p", pg);
}
#endif
KASSERT((pg->pg_flags & PG_DEV) == 0);
KASSERT(pg->uobject == NULL || UVM_OBJ_IS_DUMMY(pg->uobject) ||
rw_write_held(pg->uobject->vmobjlock));
KASSERT(pg->uobject != NULL || pg->uanon == NULL ||
rw_write_held(pg->uanon->an_lock));
if (pg->pg_flags & PG_TABLED)
uvm_pageremove(pg);
if (pg->pg_flags & (PQ_ACTIVE|PQ_INACTIVE)) {
uvm_lock_pageq();
uvm_pagedequeue(pg);
uvm_unlock_pageq();
}
if (pg->wire_count) {
pg->wire_count = 0;
atomic_dec_int(&uvmexp.wired);
}
if (pg->uanon) {
pg->uanon->an_page = NULL;
pg->uanon = NULL;
}
flags_to_clear |= PQ_ANON|PQ_AOBJ|PQ_ENCRYPT|PG_ZERO|PG_FAKE|PG_BUSY|
PG_RELEASED|PG_CLEAN|PG_CLEANCHK;
atomic_clearbits_int(&pg->pg_flags, flags_to_clear);
#ifdef DEBUG
pg->uobject = (void *)0xdeadbeef;
pg->offset = 0xdeadbeef;
pg->uanon = (void *)0xdeadbeef;
#endif
}
void
uvm_pagefree(struct vm_page *pg)
{
uvm_pageclean(pg);
uvm_pmr_cache_put(pg);
}
void
uvm_page_unbusy(struct vm_page **pgs, int npgs)
{
struct vm_page *pg;
int i;
for (i = 0; i < npgs; i++) {
pg = pgs[i];
if (pg == NULL || pg == PGO_DONTCARE) {
continue;
}
KASSERT(uvm_page_owner_locked_p(pg, TRUE));
KASSERT(pg->pg_flags & PG_BUSY);
if (pg->pg_flags & PG_WANTED) {
wakeup(pg);
}
if (pg->pg_flags & PG_RELEASED) {
KASSERT(pg->uobject != NULL ||
(pg->uanon != NULL && pg->uanon->an_ref > 0));
atomic_clearbits_int(&pg->pg_flags, PG_WANTED);
pmap_page_protect(pg, PROT_NONE);
uvm_pagefree(pg);
} else {
KASSERT((pg->pg_flags & PG_FAKE) == 0);
atomic_clearbits_int(&pg->pg_flags, PG_WANTED|PG_BUSY);
UVM_PAGE_OWN(pg, NULL);
}
}
}
void
uvm_pagewait(struct vm_page *pg, struct rwlock *lock, const char *wmesg)
{
KASSERT(rw_lock_held(lock));
KASSERT((pg->pg_flags & PG_BUSY) != 0);
KASSERT(uvm_page_owner_locked_p(pg, FALSE));
atomic_setbits_int(&pg->pg_flags, PG_WANTED);
rwsleep_nsec(pg, lock, PVM | PNORELOCK, wmesg, INFSLP);
}
#if defined(UVM_PAGE_TRKOWN)
void
uvm_page_own(struct vm_page *pg, char *tag)
{
if (tag) {
if (pg->owner_tag) {
printf("uvm_page_own: page %p already owned "
"by thread %d [%s]\n", pg,
pg->owner, pg->owner_tag);
panic("uvm_page_own");
}
pg->owner = (curproc) ? curproc->p_tid : (pid_t) -1;
pg->owner_tag = tag;
return;
}
if (pg->owner_tag == NULL) {
printf("uvm_page_own: dropping ownership of an non-owned "
"page (%p)\n", pg);
panic("uvm_page_own");
}
pg->owner_tag = NULL;
return;
}
#endif
#if VM_PHYSSEG_MAX > 1
int
vm_physseg_find(paddr_t pframe, int *offp)
{
struct vm_physseg *seg;
#if (VM_PHYSSEG_STRAT == VM_PSTRAT_BSEARCH)
int start, len, try;
for (start = 0, len = vm_nphysseg ; len != 0 ; len = len / 2) {
try = start + (len / 2);
seg = vm_physmem + try;
if (pframe >= seg->start) {
if (pframe < seg->end) {
if (offp)
*offp = pframe - seg->start;
return try;
}
start = try + 1;
len--;
} else {
}
}
return -1;
#else
int lcv;
for (lcv = 0, seg = vm_physmem; lcv < vm_nphysseg ; lcv++, seg++) {
if (pframe >= seg->start && pframe < seg->end) {
if (offp)
*offp = pframe - seg->start;
return lcv;
}
}
return -1;
#endif
}
struct vm_page *
PHYS_TO_VM_PAGE(paddr_t pa)
{
paddr_t pf = atop(pa);
int off;
int psi;
psi = vm_physseg_find(pf, &off);
return (psi == -1) ? NULL : &vm_physmem[psi].pgs[off];
}
#endif
struct vm_page *
uvm_pagelookup(struct uvm_object *obj, voff_t off)
{
struct vm_page p, *pg;
p.offset = off;
pg = RBT_FIND(uvm_objtree, &obj->memt, &p);
KASSERT(pg == NULL || obj->uo_npages != 0);
KASSERT(pg == NULL || (pg->pg_flags & PG_RELEASED) == 0 ||
(pg->pg_flags & PG_BUSY) != 0);
return (pg);
}
void
uvm_pagewire(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg, TRUE));
if (pg->wire_count == 0) {
uvm_lock_pageq();
uvm_pagedequeue(pg);
uvm_unlock_pageq();
atomic_inc_int(&uvmexp.wired);
}
KASSERT((pg->pg_flags & (PQ_INACTIVE|PQ_ACTIVE)) == 0);
pg->wire_count++;
KASSERT(pg->wire_count > 0);
}
void
uvm_pageunwire(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg, TRUE));
KASSERT(pg->wire_count != 0);
pg->wire_count--;
if (pg->wire_count == 0) {
uvm_pageactivate(pg);
atomic_dec_int(&uvmexp.wired);
}
}
void
uvm_pagedeactivate(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg, FALSE));
if (pg->wire_count > 0) {
KASSERT((pg->pg_flags & (PQ_INACTIVE|PQ_ACTIVE)) == 0);
return;
}
uvm_lock_pageq();
if (pg->pg_flags & PQ_INACTIVE) {
uvm_unlock_pageq();
return;
}
pmap_page_protect(pg, PROT_NONE);
uvm_pagedequeue(pg);
TAILQ_INSERT_TAIL(&uvm.page_inactive, pg, pageq);
atomic_setbits_int(&pg->pg_flags, PQ_INACTIVE);
atomic_inc_int(&uvmexp.inactive);
uvm_unlock_pageq();
pmap_clear_reference(pg);
if ((pg->pg_flags & PG_CLEAN) != 0 && pmap_is_modified(pg))
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
}
void
uvm_pageactivate(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg, FALSE));
if (pg->wire_count > 0) {
KASSERT((pg->pg_flags & (PQ_INACTIVE|PQ_ACTIVE)) == 0);
return;
}
uvm_lock_pageq();
uvm_pagedequeue(pg);
TAILQ_INSERT_TAIL(&uvm.page_active, pg, pageq);
atomic_setbits_int(&pg->pg_flags, PQ_ACTIVE);
atomic_inc_int(&uvmexp.active);
uvm_unlock_pageq();
}
void
uvm_pagedequeue(struct vm_page *pg)
{
KASSERT(uvm_page_owner_locked_p(pg, FALSE));
MUTEX_ASSERT_LOCKED(&uvm.pageqlock);
KASSERT(pg->wire_count == 0);
if (pg->pg_flags & PQ_ACTIVE) {
TAILQ_REMOVE(&uvm.page_active, pg, pageq);
atomic_clearbits_int(&pg->pg_flags, PQ_ACTIVE);
atomic_dec_int(&uvmexp.active);
}
if (pg->pg_flags & PQ_INACTIVE) {
TAILQ_REMOVE(&uvm.page_inactive, pg, pageq);
atomic_clearbits_int(&pg->pg_flags, PQ_INACTIVE);
atomic_dec_int(&uvmexp.inactive);
}
}
void
uvm_pagezero(struct vm_page *pg)
{
atomic_clearbits_int(&pg->pg_flags, PG_CLEAN);
pmap_zero_page(pg);
}
void
uvm_pagecopy(struct vm_page *src, struct vm_page *dst)
{
atomic_clearbits_int(&dst->pg_flags, PG_CLEAN);
pmap_copy_page(src, dst);
}
int
uvm_page_owner_locked_p(struct vm_page *pg, boolean_t exclusive)
{
if (pg->uobject != NULL) {
if (UVM_OBJ_IS_DUMMY(pg->uobject))
return 1;
return exclusive
? rw_write_held(pg->uobject->vmobjlock)
: rw_lock_held(pg->uobject->vmobjlock);
}
if (pg->uanon != NULL) {
return exclusive
? rw_write_held(pg->uanon->an_lock)
: rw_lock_held(pg->uanon->an_lock);
}
return 1;
}
psize_t
uvm_pagecount(struct uvm_constraint_range* constraint)
{
int lcv;
psize_t sz;
paddr_t low, high;
paddr_t ps_low, ps_high;
low = atop(constraint->ucr_low);
high = atop(constraint->ucr_high);
sz = 0;
for (lcv = 0; lcv < vm_nphysseg; lcv++) {
ps_low = MAX(low, vm_physmem[lcv].avail_start);
ps_high = MIN(high, vm_physmem[lcv].avail_end);
if (ps_low < ps_high)
sz += ps_high - ps_low;
}
return sz;
}