#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_anon.c,v 1.80 2020/10/25 00:05:26 chs Exp $");
#include "opt_uvmhist.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/pool.h>
#include <sys/kernel.h>
#include <sys/atomic.h>
#include <uvm/uvm.h>
#include <uvm/uvm_swap.h>
#include <uvm/uvm_pdpolicy.h>
static struct pool_cache uvm_anon_cache;
static int uvm_anon_ctor(void *, void *, int);
void
uvm_anon_init(void)
{
pool_cache_bootstrap(&uvm_anon_cache, sizeof(struct vm_anon), 0, 0,
PR_LARGECACHE, "anonpl", NULL, IPL_NONE, uvm_anon_ctor,
NULL, NULL);
}
static int
uvm_anon_ctor(void *arg, void *object, int flags)
{
struct vm_anon *anon = object;
anon->an_ref = 0;
anon->an_lock = NULL;
anon->an_page = NULL;
#if defined(VMSWAP)
anon->an_swslot = 0;
#endif
return 0;
}
struct vm_anon *
uvm_analloc(void)
{
struct vm_anon *anon;
anon = pool_cache_get(&uvm_anon_cache, PR_NOWAIT);
if (anon) {
KASSERT(anon->an_ref == 0);
KASSERT(anon->an_lock == NULL);
KASSERT(anon->an_page == NULL);
#if defined(VMSWAP)
KASSERT(anon->an_swslot == 0);
#endif
anon->an_ref = 1;
}
return anon;
}
void
uvm_anfree(struct vm_anon *anon)
{
struct vm_page *pg = anon->an_page, *pg2 __diagused;
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist,"(anon=%#jx)", (uintptr_t)anon, 0,0,0);
KASSERT(anon->an_lock == NULL || rw_write_held(anon->an_lock));
KASSERT(anon->an_ref == 0);
if (__predict_true(pg != NULL)) {
KASSERT(anon->an_lock != NULL);
if (__predict_false(pg->loan_count != 0)) {
pg2 = uvm_anon_lockloanpg(anon);
KASSERT(pg2 == pg);
}
if (__predict_false(pg->uobject != NULL)) {
mutex_enter(&pg->interlock);
KASSERT(pg->loan_count > 0);
pg->loan_count--;
pg->uanon = NULL;
mutex_exit(&pg->interlock);
rw_exit(pg->uobject->vmobjlock);
} else {
KASSERT((pg->flags & PG_RELEASED) == 0);
pmap_page_protect(pg, VM_PROT_NONE);
if (__predict_false((pg->flags & PG_BUSY) != 0)) {
pg->flags |= PG_RELEASED;
rw_obj_hold(anon->an_lock);
return;
}
uvm_pagefree(pg);
UVMHIST_LOG(maphist, "anon %#jx, page %#jx: "
"freed now!", (uintptr_t)anon, (uintptr_t)pg,
0, 0);
}
} else {
#if defined(VMSWAP)
if (anon->an_swslot > 0) {
KASSERT(uvmexp.swpgonly > 0);
atomic_dec_uint(&uvmexp.swpgonly);
}
#endif
}
anon->an_lock = NULL;
uvm_anon_dropswap(anon);
uvmpdpol_anfree(anon);
UVMHIST_LOG(maphist,"<- done!",0,0,0,0);
pool_cache_put(&uvm_anon_cache, anon);
}
struct vm_page *
uvm_anon_lockloanpg(struct vm_anon *anon)
{
struct vm_page *pg;
krw_t op;
KASSERT(rw_lock_held(anon->an_lock));
while (((pg = anon->an_page) != NULL) && pg->loan_count != 0) {
mutex_enter(&pg->interlock);
if (pg->uobject) {
if (!rw_tryenter(pg->uobject->vmobjlock, RW_WRITER)) {
mutex_exit(&pg->interlock);
op = rw_lock_op(anon->an_lock);
rw_exit(anon->an_lock);
kpause("lkloanpg", false, 1, NULL);
rw_enter(anon->an_lock, op);
continue;
}
}
if (pg->uobject == NULL && (pg->flags & PG_ANON) == 0) {
pg->flags |= PG_ANON;
pg->loan_count--;
}
mutex_exit(&pg->interlock);
break;
}
return pg;
}
#if defined(VMSWAP)
bool
uvm_anon_pagein(struct vm_amap *amap, struct vm_anon *anon)
{
struct vm_page *pg;
struct uvm_object *uobj;
KASSERT(rw_write_held(anon->an_lock));
KASSERT(anon->an_lock == amap->am_lock);
switch (uvmfault_anonget(NULL, amap, anon)) {
case 0:
KASSERT(rw_write_held(anon->an_lock));
break;
case EIO:
case ERESTART:
return false;
case ENOLCK:
panic("uvm_anon_pagein");
default:
return true;
}
pg = anon->an_page;
uobj = pg->uobject;
if (anon->an_swslot > 0) {
uvm_swap_free(anon->an_swslot, 1);
}
anon->an_swslot = 0;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
uvm_pagelock(pg);
uvm_pagedeactivate(pg);
uvm_pageunlock(pg);
rw_exit(anon->an_lock);
if (uobj) {
rw_exit(uobj->vmobjlock);
}
return false;
}
void
uvm_anon_dropswap(struct vm_anon *anon)
{
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (anon->an_swslot == 0)
return;
UVMHIST_LOG(maphist,"freeing swap for anon %#jx, paged to swslot %#jx",
(uintptr_t)anon, anon->an_swslot, 0, 0);
uvm_swap_free(anon->an_swslot, 1);
anon->an_swslot = 0;
}
#endif
void
uvm_anon_release(struct vm_anon *anon)
{
struct vm_page *pg = anon->an_page;
krwlock_t *lock;
KASSERT(rw_write_held(anon->an_lock));
KASSERT(pg != NULL);
KASSERT((pg->flags & PG_RELEASED) != 0);
KASSERT((pg->flags & PG_BUSY) != 0);
KASSERT(pg->uobject == NULL);
KASSERT(pg->uanon == anon);
KASSERT(pg->loan_count == 0);
KASSERT(anon->an_ref == 0);
if ((pg->flags & PG_PAGEOUT) != 0) {
pg->flags &= ~PG_PAGEOUT;
uvm_pageout_done(1);
}
uvm_pagefree(pg);
KASSERT(anon->an_page == NULL);
lock = anon->an_lock;
uvm_anfree(anon);
rw_exit(lock);
rw_obj_free(lock);
}