#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_fault.c,v 1.239 2026/06/23 19:29:12 skrll Exp $");
#include "opt_uvmhist.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/kernel.h>
#include <sys/mman.h>
#include <uvm/uvm.h>
#include <uvm/uvm_pdpolicy.h>
#include <uvm/uvm_rndsource.h>
struct uvm_advice {
int advice;
int nback;
int nforw;
};
static const struct uvm_advice uvmadvice[] = {
{ UVM_ADV_NORMAL, 3, 4 },
{ UVM_ADV_RANDOM, 0, 0 },
{ UVM_ADV_SEQUENTIAL, 8, 7},
};
#define UVM_MAXRANGE 16
static inline void
uvmfault_anonflush(struct vm_anon **anons, int n)
{
int lcv;
struct vm_page *pg;
for (lcv = 0; lcv < n; lcv++) {
if (anons[lcv] == NULL)
continue;
KASSERT(rw_lock_held(anons[lcv]->an_lock));
pg = anons[lcv]->an_page;
if (pg && (pg->flags & PG_BUSY) == 0) {
uvm_pagelock(pg);
uvm_pagedeactivate(pg);
uvm_pageunlock(pg);
}
}
}
static void
uvmfault_amapcopy(struct uvm_faultinfo *ufi)
{
for (;;) {
if (uvmfault_lookup(ufi, true) == false)
return;
if (UVM_ET_ISNEEDSCOPY(ufi->entry))
amap_copy(ufi->map, ufi->entry, AMAP_COPY_NOWAIT,
ufi->orig_rvaddr, ufi->orig_rvaddr + 1);
if (UVM_ET_ISNEEDSCOPY(ufi->entry)) {
uvmfault_unlockmaps(ufi, true);
uvm_wait("fltamapcopy");
continue;
}
uvmfault_unlockmaps(ufi, true);
return;
}
}
int
uvmfault_anonget(struct uvm_faultinfo *ufi, struct vm_amap *amap,
struct vm_anon *anon)
{
struct vm_page *pg;
krw_t lock_type;
int error __unused;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(rw_lock_held(anon->an_lock));
KASSERT(anon->an_lock == amap->am_lock);
cpu_count(CPU_COUNT_FLTANGET, 1);
if (anon->an_page) {
curlwp->l_ru.ru_minflt++;
} else {
curlwp->l_ru.ru_majflt++;
}
error = 0;
for (;;) {
bool we_own, locked;
we_own = false;
pg = anon->an_page;
if (pg && pg->loan_count)
pg = uvm_anon_lockloanpg(anon);
lock_type = rw_lock_op(anon->an_lock);
if (pg) {
if ((pg->flags & PG_BUSY) == 0) {
UVMHIST_LOG(maphist, "<- OK",0,0,0,0);
return 0;
}
cpu_count(CPU_COUNT_FLTPGWAIT, 1);
if (pg->uobject) {
uvmfault_unlockall(ufi, amap, NULL);
UVMHIST_LOG(maphist, " unlock+wait on uobj",0,
0,0,0);
uvm_pagewait(pg, pg->uobject->vmobjlock, "anonget1");
} else {
uvmfault_unlockall(ufi, NULL, NULL);
UVMHIST_LOG(maphist, " unlock+wait on anon",0,
0,0,0);
uvm_pagewait(pg, anon->an_lock, "anonget2");
}
} else {
#if defined(VMSWAP)
if (lock_type == RW_READER) {
return ENOLCK;
}
pg = uvm_pagealloc(NULL,
ufi != NULL ? ufi->orig_rvaddr : 0,
anon, ufi != NULL ? UVM_FLAG_COLORMATCH : 0);
if (pg == NULL) {
uvmfault_unlockall(ufi, amap, NULL);
cpu_count(CPU_COUNT_FLTNORAM, 1);
UVMHIST_LOG(maphist, " noram -- UVM_WAIT",0,
0,0,0);
if (!uvm_reclaimable()) {
return ENOMEM;
}
uvm_wait("flt_noram1");
} else {
we_own = true;
uvmfault_unlockall(ufi, amap, NULL);
cpu_count(CPU_COUNT_PAGEINS, 1);
error = uvm_swap_get(pg, anon->an_swslot,
PGO_SYNCIO);
}
#else
panic("%s: no page", __func__);
#endif
}
locked = uvmfault_relock(ufi);
if (locked || we_own) {
rw_enter(anon->an_lock, lock_type);
}
if (we_own) {
KASSERT(lock_type == RW_WRITER);
#if defined(VMSWAP)
if (error) {
if (anon->an_swslot > 0) {
uvm_swap_markbad(anon->an_swslot, 1);
}
anon->an_swslot = SWSLOT_BAD;
if ((pg->flags & PG_RELEASED) != 0) {
goto released;
}
uvm_pagefree(pg);
if (locked) {
uvmfault_unlockall(ufi, NULL, NULL);
}
rw_exit(anon->an_lock);
UVMHIST_LOG(maphist, "<- ERROR", 0,0,0,0);
return error;
}
if ((pg->flags & PG_RELEASED) != 0) {
released:
KASSERT(anon->an_ref == 0);
if (locked) {
uvmfault_unlockall(ufi, NULL, NULL);
}
uvm_anon_release(anon);
if (error) {
UVMHIST_LOG(maphist,
"<- ERROR/RELEASED", 0,0,0,0);
return error;
}
UVMHIST_LOG(maphist, "<- RELEASED", 0,0,0,0);
return ERESTART;
}
uvm_pagelock(pg);
uvm_pageactivate(pg);
uvm_pagewakeup(pg);
uvm_pageunlock(pg);
pg->flags &= ~(PG_BUSY|PG_FAKE);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_UNKNOWN);
UVM_PAGE_OWN(pg, NULL);
#else
panic("%s: we_own", __func__);
#endif
}
if (!locked) {
if (we_own) {
rw_exit(anon->an_lock);
}
UVMHIST_LOG(maphist, "<- REFAULT", 0,0,0,0);
return ERESTART;
}
if (ufi != NULL && amap_lookup(&ufi->entry->aref,
ufi->orig_rvaddr - ufi->entry->start) != anon) {
uvmfault_unlockall(ufi, amap, NULL);
UVMHIST_LOG(maphist, "<- REFAULT", 0,0,0,0);
return ERESTART;
}
cpu_count(CPU_COUNT_FLTANRETRY, 1);
continue;
}
}
static int
uvmfault_promote(struct uvm_faultinfo *ufi,
struct vm_anon *oanon,
struct vm_page *uobjpage,
struct vm_anon **nanon,
struct vm_anon **spare)
{
struct vm_amap *amap = ufi->entry->aref.ar_amap;
struct uvm_object *uobj;
struct vm_anon *anon;
struct vm_page *pg;
struct vm_page *opg;
int error;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (oanon) {
opg = oanon->an_page;
KASSERT(opg != NULL);
KASSERT(opg->uobject == NULL || opg->loan_count > 0);
} else if (uobjpage != PGO_DONTCARE) {
opg = uobjpage;
KASSERT(rw_lock_held(opg->uobject->vmobjlock));
} else {
opg = NULL;
}
if (opg != NULL) {
uobj = opg->uobject;
} else {
uobj = NULL;
}
KASSERT(amap != NULL);
KASSERT(uobjpage != NULL);
KASSERT(rw_write_held(amap->am_lock));
KASSERT(oanon == NULL || amap->am_lock == oanon->an_lock);
KASSERT(uobj == NULL || rw_lock_held(uobj->vmobjlock));
if (*spare != NULL) {
anon = *spare;
*spare = NULL;
} else {
anon = uvm_analloc();
}
if (anon) {
KASSERT(anon->an_lock == NULL);
anon->an_lock = amap->am_lock;
pg = uvm_pagealloc(NULL, ufi->orig_rvaddr, anon,
UVM_FLAG_COLORMATCH | (opg == NULL ? UVM_PGA_ZERO : 0));
if (pg == NULL) {
anon->an_lock = NULL;
}
} else {
pg = NULL;
}
if (pg == NULL) {
if (anon != NULL) {
*spare = anon;
}
uvmfault_unlockall(ufi, amap, uobj);
if (!uvm_reclaimable()) {
UVMHIST_LOG(maphist, "out of VM", 0,0,0,0);
cpu_count(CPU_COUNT_FLTNOANON, 1);
error = ENOMEM;
goto done;
}
UVMHIST_LOG(maphist, "out of RAM, waiting for more", 0,0,0,0);
cpu_count(CPU_COUNT_FLTNORAM, 1);
uvm_wait("flt_noram5");
error = ERESTART;
goto done;
}
if (opg) {
pmap_remove(vm_map_pmap(ufi->orig_map), ufi->orig_rvaddr,
ufi->orig_rvaddr + PAGE_SIZE);
pmap_update(vm_map_pmap(ufi->orig_map));
uvm_pagecopy(opg, pg);
}
KASSERT(uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_DIRTY);
amap_add(&ufi->entry->aref, ufi->orig_rvaddr - ufi->entry->start, anon,
oanon != NULL);
pg = anon->an_page;
pg->flags &= ~(PG_BUSY|PG_FAKE);
UVM_PAGE_OWN(pg, NULL);
*nanon = anon;
error = 0;
done:
return error;
}
void
uvmfault_update_stats(struct uvm_faultinfo *ufi)
{
struct vm_map *map;
struct vmspace *vm;
struct proc *p;
vsize_t res;
map = ufi->orig_map;
p = curproc;
KASSERT(p != NULL);
vm = p->p_vmspace;
if (&vm->vm_map != map)
return;
res = pmap_resident_count(map->pmap);
if (vm->vm_rssmax < res)
vm->vm_rssmax = res;
}
#define MASK(entry) (UVM_ET_ISCOPYONWRITE(entry) ? \
~VM_PROT_WRITE : VM_PROT_ALL)
#define UVM_FAULT_WIRE (1 << 0)
#define UVM_FAULT_MAXPROT (1 << 1)
struct uvm_faultctx {
vm_prot_t access_type;
vaddr_t startva;
int npages;
int centeridx;
bool narrow;
bool wire_mapping;
bool wire_paging;
bool cow_now;
vm_prot_t enter_prot;
struct vm_anon *anon_spare;
bool promote;
krw_t lower_lock_type;
krw_t upper_lock_type;
};
static inline int uvm_fault_check(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct vm_anon ***, bool);
static int uvm_fault_upper(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct vm_anon **);
static inline int uvm_fault_upper_lookup(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct vm_anon **, struct vm_page **);
static inline void uvm_fault_upper_neighbor(
struct uvm_faultinfo *, const struct uvm_faultctx *,
vaddr_t, struct vm_page *, bool);
static inline int uvm_fault_upper_loan(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct vm_anon *, struct uvm_object **);
static inline int uvm_fault_upper_promote(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct uvm_object *, struct vm_anon *);
static inline int uvm_fault_upper_direct(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct uvm_object *, struct vm_anon *);
static int uvm_fault_upper_enter(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct uvm_object *, struct vm_anon *,
struct vm_page *, struct vm_anon *);
static inline void uvm_fault_upper_done(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct vm_anon *, struct vm_page *);
static int uvm_fault_lower(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct vm_page **);
static inline void uvm_fault_lower_lookup(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct vm_page **);
static inline void uvm_fault_lower_neighbor(
struct uvm_faultinfo *, const struct uvm_faultctx *,
vaddr_t, struct vm_page *);
static inline int uvm_fault_lower_io(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct uvm_object **, struct vm_page **);
static inline int uvm_fault_lower_direct(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct uvm_object *, struct vm_page *);
static inline int uvm_fault_lower_direct_loan(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct uvm_object *, struct vm_page **,
struct vm_page **);
static inline int uvm_fault_lower_promote(
struct uvm_faultinfo *, struct uvm_faultctx *,
struct uvm_object *, struct vm_page *);
static int uvm_fault_lower_enter(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct uvm_object *,
struct vm_anon *, struct vm_page *);
static inline void uvm_fault_lower_done(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct uvm_object *, struct vm_page *);
int
uvm_fault_internal(struct vm_map *orig_map, vaddr_t vaddr,
vm_prot_t access_type, int fault_flag)
{
struct uvm_faultinfo ufi;
struct uvm_faultctx flt = {
.access_type = access_type,
.narrow = (fault_flag & UVM_FAULT_WIRE) != 0,
.wire_mapping = (fault_flag & UVM_FAULT_WIRE) != 0,
.wire_paging = (fault_flag & UVM_FAULT_WIRE) != 0,
#ifdef __HAVE_UNLOCKED_PMAP
.upper_lock_type = RW_WRITER,
.lower_lock_type = RW_WRITER,
#else
.upper_lock_type = RW_READER,
.lower_lock_type = RW_READER,
#endif
};
const bool maxprot = (fault_flag & UVM_FAULT_MAXPROT) != 0;
struct vm_anon *anons_store[UVM_MAXRANGE], **anons;
struct vm_page *pages_store[UVM_MAXRANGE], **pages;
int error;
UVMHIST_FUNC(__func__);
UVMHIST_CALLARGS(maphist, "(map=%#jx, vaddr=%#jx, at=%jd, ff=%jd)",
(uintptr_t)orig_map, vaddr, access_type, fault_flag);
kpreempt_disable();
if (__predict_true(start_init_exec)) {
struct cpu_info *ci = curcpu();
CPU_COUNT(CPU_COUNT_NFAULT, 1);
if (++(ci->ci_faultrng) == 503) {
ci->ci_faultrng = 0;
rnd_add_uint32(&uvm_fault_rndsource,
sizeof(vaddr_t) == sizeof(uint32_t) ?
(uint32_t)vaddr : sizeof(vaddr_t) ==
sizeof(uint64_t) ?
(uint32_t)vaddr :
(uint32_t)ci->ci_counts[CPU_COUNT_NFAULT]);
}
}
kpreempt_enable();
ufi.orig_map = orig_map;
ufi.orig_rvaddr = trunc_page(vaddr);
ufi.orig_size = PAGE_SIZE;
error = ERESTART;
while (error == ERESTART) {
anons = anons_store;
pages = pages_store;
error = uvm_fault_check(&ufi, &flt, &anons, maxprot);
if (error != 0)
continue;
error = uvm_fault_upper_lookup(&ufi, &flt, anons, pages);
if (error != 0)
continue;
if (pages[flt.centeridx] == PGO_DONTCARE)
error = uvm_fault_upper(&ufi, &flt, anons);
else {
struct uvm_object * const uobj =
ufi.entry->object.uvm_obj;
if (uobj && uobj->pgops->pgo_fault != NULL) {
rw_enter(uobj->vmobjlock, RW_WRITER);
error = uobj->pgops->pgo_fault(&ufi,
flt.startva, pages, flt.npages,
flt.centeridx, flt.access_type,
PGO_LOCKED|PGO_SYNCIO);
if (error == ENOMEM && uvm_reclaimable()) {
uvm_wait("pgo_fault");
error = ERESTART;
}
} else {
error = uvm_fault_lower(&ufi, &flt, pages);
}
}
}
if (flt.anon_spare != NULL) {
flt.anon_spare->an_ref--;
KASSERT(flt.anon_spare->an_ref == 0);
KASSERT(flt.anon_spare->an_lock == NULL);
uvm_anfree(flt.anon_spare);
}
return error;
}
static int
uvm_fault_check(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_anon ***ranons, bool maxprot)
{
struct vm_amap *amap;
struct uvm_object *uobj;
vm_prot_t check_prot;
int nback, nforw;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (uvmfault_lookup(ufi, false) == false) {
UVMHIST_LOG(maphist, "<- no mapping @ %#jx", ufi->orig_rvaddr,
0,0,0);
return EFAULT;
}
#ifdef DIAGNOSTIC
if ((ufi->map->flags & VM_MAP_PAGEABLE) == 0) {
printf("Page fault on non-pageable map:\n");
printf("ufi->map = %p\n", ufi->map);
printf("ufi->orig_map = %p\n", ufi->orig_map);
printf("ufi->orig_rvaddr = %#lx\n", (u_long) ufi->orig_rvaddr);
panic("uvm_fault: (ufi->map->flags & VM_MAP_PAGEABLE) == 0");
}
#endif
check_prot = maxprot ?
ufi->entry->max_protection : ufi->entry->protection;
if ((check_prot & flt->access_type) != flt->access_type) {
UVMHIST_LOG(maphist,
"<- protection failure (prot=%#jx, access=%#jx)",
ufi->entry->protection, flt->access_type, 0, 0);
uvmfault_unlockmaps(ufi, false);
return EFAULT;
}
flt->enter_prot = ufi->entry->protection;
if (VM_MAPENT_ISWIRED(ufi->entry)) {
flt->wire_mapping = true;
flt->wire_paging = true;
flt->narrow = true;
}
if (flt->wire_mapping) {
flt->access_type = flt->enter_prot;
flt->cow_now = (check_prot & VM_PROT_WRITE) != 0;
} else {
flt->cow_now = (flt->access_type & VM_PROT_WRITE) != 0;
}
if (flt->wire_paging) {
flt->upper_lock_type = RW_WRITER;
flt->lower_lock_type = RW_WRITER;
}
flt->promote = false;
if (UVM_ET_ISNEEDSCOPY(ufi->entry)) {
if (flt->cow_now || (ufi->entry->object.uvm_obj == NULL)) {
KASSERT(!maxprot);
UVMHIST_LOG(maphist,
" need to clear needs_copy and refault",0,0,0,0);
uvmfault_unlockmaps(ufi, false);
uvmfault_amapcopy(ufi);
cpu_count(CPU_COUNT_FLTAMCOPY, 1);
return ERESTART;
} else {
flt->enter_prot &= ~VM_PROT_WRITE;
}
}
amap = ufi->entry->aref.ar_amap;
uobj = ufi->entry->object.uvm_obj;
if (amap == NULL && uobj == NULL) {
uvmfault_unlockmaps(ufi, false);
UVMHIST_LOG(maphist,"<- no backing store, no overlay",0,0,0,0);
return EFAULT;
}
if (uobj != NULL && amap != NULL &&
(flt->access_type & VM_PROT_WRITE) != 0) {
flt->narrow = true;
}
if (flt->narrow == false) {
KASSERT(uvmadvice[ufi->entry->advice].advice ==
ufi->entry->advice);
nback = MIN(uvmadvice[ufi->entry->advice].nback,
(ufi->orig_rvaddr - ufi->entry->start) >> PAGE_SHIFT);
flt->startva = ufi->orig_rvaddr - (nback << PAGE_SHIFT);
nforw = MIN(uvmadvice[ufi->entry->advice].nforw,
((ufi->entry->end - ufi->orig_rvaddr) >>
PAGE_SHIFT) - 1);
flt->npages = nback + nforw + 1;
flt->centeridx = nback;
flt->narrow = true;
} else {
nback = nforw = 0;
flt->startva = ufi->orig_rvaddr;
flt->npages = 1;
flt->centeridx = 0;
}
const voff_t eoff = flt->startva - ufi->entry->start;
UVMHIST_LOG(maphist, " narrow=%jd, back=%jd, forw=%jd, startva=%#jx",
flt->narrow, nback, nforw, flt->startva);
UVMHIST_LOG(maphist, " entry=%#jx, amap=%#jx, obj=%#jx",
(uintptr_t)ufi->entry, (uintptr_t)amap, (uintptr_t)uobj, 0);
if (amap) {
if ((amap_flags(amap) & AMAP_SHARED) == 0) {
flt->upper_lock_type = RW_WRITER;
} else if ((flt->access_type & VM_PROT_WRITE) != 0) {
flt->upper_lock_type = RW_WRITER;
}
amap_lock(amap, flt->upper_lock_type);
amap_lookups(&ufi->entry->aref, eoff, *ranons, flt->npages);
} else {
if ((flt->access_type & VM_PROT_WRITE) != 0) {
flt->lower_lock_type = RW_WRITER;
}
*ranons = NULL;
}
KASSERT(amap == NULL ||
rw_lock_op(amap->am_lock) == flt->upper_lock_type);
if (ufi->entry->advice == MADV_SEQUENTIAL && nback != 0) {
UVMHIST_LOG(maphist, " MADV_SEQUENTIAL: flushing backpages",
0,0,0,0);
if (amap)
uvmfault_anonflush(*ranons, nback);
if (uobj) {
voff_t uoff;
flt->lower_lock_type = RW_WRITER;
uoff = ufi->entry->offset + eoff;
rw_enter(uobj->vmobjlock, RW_WRITER);
(void) (uobj->pgops->pgo_put)(uobj, uoff, uoff +
(nback << PAGE_SHIFT), PGO_DEACTIVATE);
}
if (amap)
*ranons += nback;
flt->startva += (nback << PAGE_SHIFT);
flt->npages -= nback;
flt->centeridx = 0;
}
KASSERT(flt->startva <= ufi->orig_rvaddr);
KASSERT(ufi->orig_rvaddr + ufi->orig_size <=
flt->startva + (flt->npages << PAGE_SHIFT));
return 0;
}
static inline int
uvm_fault_upper_upgrade(struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_amap *amap, struct uvm_object *uobj)
{
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(amap != NULL);
KASSERT(flt->upper_lock_type == rw_lock_op(amap->am_lock));
if (__predict_true(flt->upper_lock_type == RW_WRITER)) {
return 0;
}
flt->upper_lock_type = RW_WRITER;
if (__predict_false(!rw_tryupgrade(amap->am_lock))) {
uvmfault_unlockall(ufi, amap, uobj);
cpu_count(CPU_COUNT_FLTNOUP, 1);
UVMHIST_LOG(maphist, " !upgrade upper", 0, 0,0,0);
return ERESTART;
}
cpu_count(CPU_COUNT_FLTUP, 1);
KASSERT(flt->upper_lock_type == rw_lock_op(amap->am_lock));
return 0;
}
static int
uvm_fault_upper_lookup(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
struct vm_anon **anons, struct vm_page **pages)
{
struct vm_amap *amap = ufi->entry->aref.ar_amap;
int lcv;
vaddr_t currva;
bool shadowed __unused;
bool entered;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(amap == NULL ||
rw_lock_op(amap->am_lock) == flt->upper_lock_type);
currva = flt->startva;
shadowed = false;
entered = false;
for (lcv = 0; lcv < flt->npages; lcv++, currva += PAGE_SIZE) {
if (amap == NULL || anons[lcv] == NULL) {
pages[lcv] = NULL;
continue;
}
pages[lcv] = PGO_DONTCARE;
if (lcv == flt->centeridx) {
shadowed = true;
continue;
}
struct vm_anon *anon = anons[lcv];
struct vm_page *pg = anon->an_page;
KASSERT(anon->an_lock == amap->am_lock);
if (pg && pg->loan_count == 0 && (pg->flags & PG_BUSY) == 0 &&
!pmap_extract(ufi->orig_map->pmap, currva, NULL)) {
uvm_fault_upper_neighbor(ufi, flt, currva,
pg, anon->an_ref > 1);
entered = true;
}
}
if (entered) {
pmap_update(ufi->orig_map->pmap);
}
KASSERT(amap == NULL ||
rw_lock_op(amap->am_lock) == flt->upper_lock_type);
UVMHIST_LOG(maphist, " shadowed=%jd, will_get=%jd", shadowed,
(ufi->entry->object.uvm_obj && shadowed != false),0,0);
return 0;
}
static void
uvm_fault_upper_neighbor(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
vaddr_t currva, struct vm_page *pg, bool readonly)
{
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(pg->uobject == NULL);
KASSERT(pg->uanon != NULL);
KASSERT(rw_lock_op(pg->uanon->an_lock) == flt->upper_lock_type);
KASSERT(uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN);
if (!uvmpdpol_pageisqueued_p(pg) && pg->wire_count == 0) {
uvm_pagelock(pg);
uvm_pageenqueue(pg);
uvm_pageunlock(pg);
}
UVMHIST_LOG(maphist,
" MAPPING: n anon: pm=%#jx, va=%#jx, pg=%#jx",
(uintptr_t)ufi->orig_map->pmap, currva, (uintptr_t)pg, 0);
cpu_count(CPU_COUNT_FLTNAMAP, 1);
(void) pmap_enter(ufi->orig_map->pmap, currva,
VM_PAGE_TO_PHYS(pg),
readonly ? (flt->enter_prot & ~VM_PROT_WRITE) :
flt->enter_prot,
PMAP_CANFAIL | (flt->wire_mapping ? PMAP_WIRED : 0));
}
static int
uvm_fault_upper(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_anon **anons)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
struct vm_anon * const anon = anons[flt->centeridx];
struct uvm_object *uobj;
int error;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(rw_lock_op(amap->am_lock) == flt->upper_lock_type);
KASSERT(anon->an_lock == amap->am_lock);
UVMHIST_LOG(maphist, " case 1 fault: anon=%#jx",
(uintptr_t)anon, 0, 0, 0);
retry:
error = uvmfault_anonget(ufi, amap, anon);
switch (error) {
case 0:
break;
case ERESTART:
return ERESTART;
case EAGAIN:
kpause("fltagain1", false, hz/2, NULL);
return ERESTART;
case ENOLCK:
error = uvm_fault_upper_upgrade(ufi, flt, amap, NULL);
if (error != 0) {
return error;
}
KASSERT(rw_write_held(amap->am_lock));
goto retry;
default:
return error;
}
uobj = anon->an_page->uobject;
KASSERT(rw_lock_op(amap->am_lock) == flt->upper_lock_type);
KASSERT(anon->an_lock == amap->am_lock);
KASSERT(uobj == NULL ||
rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
if (anon->an_page->loan_count) {
error = uvm_fault_upper_loan(ufi, flt, anon, &uobj);
if (error != 0)
return error;
}
if (flt->cow_now && anon->an_ref > 1) {
flt->promote = true;
error = uvm_fault_upper_promote(ufi, flt, uobj, anon);
} else {
error = uvm_fault_upper_direct(ufi, flt, uobj, anon);
}
return error;
}
static int
uvm_fault_upper_loan(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_anon *anon, struct uvm_object **ruobj)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
int error = 0;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (!flt->cow_now) {
flt->enter_prot = flt->enter_prot & ~VM_PROT_WRITE;
} else {
if (anon->an_ref == 1) {
error = uvm_fault_upper_upgrade(ufi, flt, amap, NULL);
if (error != 0) {
return error;
}
KASSERT(rw_write_held(amap->am_lock));
error = uvm_loanbreak_anon(anon, *ruobj);
if (error != 0) {
uvmfault_unlockall(ufi, amap, *ruobj);
uvm_wait("flt_noram2");
return ERESTART;
}
if (*ruobj)
*ruobj = NULL;
}
}
return error;
}
static int
uvm_fault_upper_promote(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct uvm_object *uobj, struct vm_anon *anon)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
struct vm_anon * const oanon = anon;
struct vm_page *pg;
int error;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
UVMHIST_LOG(maphist, " case 1B: COW fault",0,0,0,0);
error = uvm_fault_upper_upgrade(ufi, flt, amap, NULL);
if (error != 0) {
return error;
}
KASSERT(rw_write_held(amap->am_lock));
cpu_count(CPU_COUNT_FLT_ACOW, 1);
error = uvmfault_promote(ufi, oanon, PGO_DONTCARE, &anon,
&flt->anon_spare);
switch (error) {
case 0:
break;
case ERESTART:
return ERESTART;
default:
return error;
}
pg = anon->an_page;
KASSERT(anon->an_lock == oanon->an_lock);
KASSERT((pg->flags & (PG_BUSY | PG_FAKE)) == 0);
KASSERT(oanon->an_ref > 1);
oanon->an_ref--;
return uvm_fault_upper_enter(ufi, flt, uobj, anon, pg, oanon);
}
static int
uvm_fault_upper_direct(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct uvm_object *uobj, struct vm_anon *anon)
{
struct vm_anon * const oanon = anon;
struct vm_page *pg;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
cpu_count(CPU_COUNT_FLT_ANON, 1);
pg = anon->an_page;
if (anon->an_ref > 1)
flt->enter_prot = flt->enter_prot & ~VM_PROT_WRITE;
return uvm_fault_upper_enter(ufi, flt, uobj, anon, pg, oanon);
}
static int
uvm_fault_upper_enter(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
struct uvm_object *uobj, struct vm_anon *anon, struct vm_page *pg,
struct vm_anon *oanon)
{
struct pmap *pmap = ufi->orig_map->pmap;
vaddr_t va = ufi->orig_rvaddr;
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(rw_lock_op(amap->am_lock) == flt->upper_lock_type);
KASSERT(anon->an_lock == amap->am_lock);
KASSERT(oanon->an_lock == amap->am_lock);
KASSERT(uobj == NULL ||
rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
KASSERT(uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN);
UVMHIST_LOG(maphist,
" MAPPING: anon: pm=%#jx, va=%#jx, pg=%#jx, promote=%jd",
(uintptr_t)pmap, va, (uintptr_t)pg, flt->promote);
if (pmap_enter(pmap, va, VM_PAGE_TO_PHYS(pg),
flt->enter_prot, flt->access_type | PMAP_CANFAIL |
(flt->wire_mapping ? PMAP_WIRED : 0)) != 0) {
uvm_pagelock(pg);
uvm_pageenqueue(pg);
uvm_pageunlock(pg);
uvmfault_unlockall(ufi, amap, uobj);
if (!uvm_reclaimable()) {
UVMHIST_LOG(maphist,
"<- failed. out of VM",0,0,0,0);
return ENOMEM;
}
uvm_wait("flt_pmfail1");
return ERESTART;
}
uvm_fault_upper_done(ufi, flt, anon, pg);
pmap_update(pmap);
uvmfault_unlockall(ufi, amap, uobj);
return 0;
}
static void
uvm_fault_upper_done(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
struct vm_anon *anon, struct vm_page *pg)
{
const bool wire_paging = flt->wire_paging;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (wire_paging) {
uvm_pagelock(pg);
uvm_pagewire(pg);
uvm_pageunlock(pg);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
uvm_anon_dropswap(anon);
} else if (uvmpdpol_pageactivate_p(pg)) {
uvm_pagelock(pg);
uvm_pageactivate(pg);
uvm_pageunlock(pg);
}
}
static inline int
uvm_fault_lower_upgrade(struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_amap *amap, struct uvm_object *uobj, struct vm_page *uobjpage)
{
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(uobj != NULL);
KASSERT(flt->lower_lock_type == rw_lock_op(uobj->vmobjlock));
if (__predict_true(flt->lower_lock_type == RW_WRITER)) {
return 0;
}
flt->lower_lock_type = RW_WRITER;
if (__predict_false(!rw_tryupgrade(uobj->vmobjlock))) {
uvmfault_unlockall(ufi, amap, uobj);
cpu_count(CPU_COUNT_FLTNOUP, 1);
UVMHIST_LOG(maphist, " !upgrade lower", 0, 0,0,0);
return ERESTART;
}
cpu_count(CPU_COUNT_FLTUP, 1);
KASSERT(flt->lower_lock_type == rw_lock_op(uobj->vmobjlock));
return 0;
}
static int
uvm_fault_lower(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct vm_page **pages)
{
struct vm_amap *amap __diagused = ufi->entry->aref.ar_amap;
struct uvm_object *uobj = ufi->entry->object.uvm_obj;
struct vm_page *uobjpage;
int error;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (uobj == NULL) {
uobjpage = NULL;
} else {
uvm_fault_lower_lookup(ufi, flt, pages);
uobjpage = pages[flt->centeridx];
}
KASSERT(amap == NULL ||
rw_lock_op(amap->am_lock) == flt->upper_lock_type);
KASSERT(uobj == NULL ||
rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
if (uobj == NULL) {
uobjpage = PGO_DONTCARE;
flt->promote = true;
} else {
KASSERT(uobjpage != PGO_DONTCARE);
flt->promote = flt->cow_now && UVM_ET_ISCOPYONWRITE(ufi->entry);
}
UVMHIST_LOG(maphist, " case 2 fault: promote=%jd, zfill=%jd",
flt->promote, (uobj == NULL), 0,0);
if (uobjpage) {
curlwp->l_ru.ru_minflt++;
} else {
error = uvm_fault_lower_io(ufi, flt, &uobj, &uobjpage);
if (error != 0)
return error;
}
KASSERT(amap == NULL ||
rw_lock_op(amap->am_lock) == flt->upper_lock_type);
KASSERT(uobj == NULL ||
rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
KASSERT(uobjpage != NULL);
KASSERT(uobj == NULL ||
uobjpage->uobject->vmobjlock == uobj->vmobjlock);
KASSERT(uobj == NULL || !UVM_OBJ_IS_CLEAN(uobjpage->uobject) ||
uvm_pagegetdirty(uobjpage) == UVM_PAGE_STATUS_CLEAN);
if (!flt->promote) {
error = uvm_fault_lower_direct(ufi, flt, uobj, uobjpage);
} else {
error = uvm_fault_lower_promote(ufi, flt, uobj, uobjpage);
}
return error;
}
static void
uvm_fault_lower_lookup(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
struct vm_page **pages)
{
struct uvm_object *uobj = ufi->entry->object.uvm_obj;
int lcv, gotpages;
vaddr_t currva;
bool entered;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
rw_enter(uobj->vmobjlock, flt->lower_lock_type);
cpu_count(CPU_COUNT_FLTLGET, 1);
gotpages = flt->npages;
(void) uobj->pgops->pgo_get(uobj,
ufi->entry->offset + flt->startva - ufi->entry->start,
pages, &gotpages, flt->centeridx,
flt->access_type & MASK(ufi->entry), ufi->entry->advice,
PGO_LOCKED);
KASSERT(rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
if (gotpages == 0) {
pages[flt->centeridx] = NULL;
return;
}
entered = false;
currva = flt->startva;
for (lcv = 0; lcv < flt->npages; lcv++, currva += PAGE_SIZE) {
struct vm_page *curpg;
curpg = pages[lcv];
if (curpg == NULL || curpg == PGO_DONTCARE) {
continue;
}
KASSERT(curpg->uobject->vmobjlock == uobj->vmobjlock);
KASSERT((curpg->flags & PG_BUSY) == 0);
if (lcv == flt->centeridx) {
UVMHIST_LOG(maphist, " got uobjpage (%#jx) "
"with locked get", (uintptr_t)curpg, 0, 0, 0);
} else if (!pmap_extract(ufi->orig_map->pmap, currva, NULL)) {
uvm_fault_lower_neighbor(ufi, flt, currva, curpg);
entered = true;
}
}
if (entered) {
pmap_update(ufi->orig_map->pmap);
}
}
static void
uvm_fault_lower_neighbor(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
vaddr_t currva, struct vm_page *pg)
{
const bool readonly = uvm_pagereadonly_p(pg) || pg->loan_count > 0;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (!uvmpdpol_pageisqueued_p(pg) && pg->wire_count == 0) {
uvm_pagelock(pg);
uvm_pageenqueue(pg);
uvm_pageunlock(pg);
}
UVMHIST_LOG(maphist,
" MAPPING: n obj: pm=%#jx, va=%#jx, pg=%#jx",
(uintptr_t)ufi->orig_map->pmap, currva, (uintptr_t)pg, 0);
cpu_count(CPU_COUNT_FLTNOMAP, 1);
KASSERT((pg->flags & PG_PAGEOUT) == 0);
KASSERT((pg->flags & PG_RELEASED) == 0);
KASSERT(!UVM_OBJ_IS_CLEAN(pg->uobject) ||
uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_CLEAN);
KASSERT((pg->flags & PG_BUSY) == 0);
KASSERT(rw_lock_op(pg->uobject->vmobjlock) == flt->lower_lock_type);
const vm_prot_t mapprot =
readonly ? (flt->enter_prot & ~VM_PROT_WRITE) :
flt->enter_prot & MASK(ufi->entry);
const u_int mapflags =
PMAP_CANFAIL | (flt->wire_mapping ? (mapprot | PMAP_WIRED) : 0);
(void) pmap_enter(ufi->orig_map->pmap, currva,
VM_PAGE_TO_PHYS(pg), mapprot, mapflags);
}
static int
uvm_fault_lower_io(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct uvm_object **ruobj, struct vm_page **ruobjpage)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
struct uvm_object *uobj = *ruobj;
struct vm_page *pg;
bool locked;
int gotpages;
int error;
voff_t uoff;
vm_prot_t access_type;
int advice;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
uoff = (ufi->orig_rvaddr - ufi->entry->start) + ufi->entry->offset;
access_type = flt->access_type & MASK(ufi->entry);
advice = ufi->entry->advice;
KASSERT(rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
error = uvm_fault_lower_upgrade(ufi, flt, amap, uobj, NULL);
if (error != 0) {
return error;
}
uvmfault_unlockall(ufi, amap, NULL);
curlwp->l_ru.ru_majflt++;
KASSERT(rw_write_held(uobj->vmobjlock));
cpu_count(CPU_COUNT_FLTGET, 1);
gotpages = 1;
pg = NULL;
error = uobj->pgops->pgo_get(uobj, uoff, &pg, &gotpages,
0, access_type, advice, PGO_SYNCIO);
if (error) {
if (error == EAGAIN) {
UVMHIST_LOG(maphist,
" pgo_get says TRY AGAIN!",0,0,0,0);
kpause("fltagain2", false, hz/2, NULL);
return ERESTART;
}
#if 0
KASSERT(error != ERESTART);
#else
if (error == ERESTART)
error = EIO;
#endif
UVMHIST_LOG(maphist, "<- pgo_get failed (code %jd)",
error, 0,0,0);
return error;
}
locked = uvmfault_relock(ufi);
if (locked && amap)
amap_lock(amap, flt->upper_lock_type);
uobj = pg->uobject;
rw_enter(uobj->vmobjlock, flt->lower_lock_type);
KASSERT((pg->flags & PG_BUSY) != 0);
KASSERT(flt->lower_lock_type == RW_WRITER);
uvm_pagelock(pg);
uvm_pageactivate(pg);
uvm_pageunlock(pg);
if ((pg->flags & PG_RELEASED) != 0 ||
(locked && amap && amap_lookup(&ufi->entry->aref,
ufi->orig_rvaddr - ufi->entry->start))) {
if (locked)
uvmfault_unlockall(ufi, amap, NULL);
locked = false;
}
if ((pg->flags & PG_RELEASED) == 0) {
pg->flags &= ~PG_BUSY;
uvm_pagelock(pg);
uvm_pagewakeup(pg);
uvm_pageunlock(pg);
UVM_PAGE_OWN(pg, NULL);
} else {
cpu_count(CPU_COUNT_FLTPGRELE, 1);
uvm_pagefree(pg);
}
if (locked == false) {
UVMHIST_LOG(maphist,
" wasn't able to relock after fault: retry",
0,0,0,0);
rw_exit(uobj->vmobjlock);
return ERESTART;
}
*ruobj = uobj;
*ruobjpage = pg;
return 0;
}
int
uvm_fault_lower_direct(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct uvm_object *uobj, struct vm_page *uobjpage)
{
struct vm_page *pg;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
cpu_count(CPU_COUNT_FLT_OBJ, 1);
if (UVM_ET_ISCOPYONWRITE(ufi->entry) ||
UVM_OBJ_NEEDS_WRITEFAULT(uobjpage->uobject))
flt->enter_prot &= ~VM_PROT_WRITE;
pg = uobjpage;
KASSERT(uobjpage != PGO_DONTCARE);
if (uobjpage->loan_count) {
uvm_fault_lower_direct_loan(ufi, flt, uobj, &pg, &uobjpage);
}
KASSERT(pg == uobjpage);
KASSERT((pg->flags & PG_BUSY) == 0);
return uvm_fault_lower_enter(ufi, flt, uobj, NULL, pg);
}
static int
uvm_fault_lower_direct_loan(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct uvm_object *uobj, struct vm_page **rpg,
struct vm_page **ruobjpage)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
struct vm_page *pg;
struct vm_page *uobjpage = *ruobjpage;
int error;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (!flt->cow_now) {
flt->enter_prot = flt->enter_prot & ~VM_PROT_WRITE;
} else {
error = uvm_fault_lower_upgrade(ufi, flt, amap, uobj, uobjpage);
if (error != 0) {
return error;
}
KASSERT(rw_write_held(uobj->vmobjlock));
pg = uvm_loanbreak(uobjpage);
if (pg == NULL) {
uvmfault_unlockall(ufi, amap, uobj);
UVMHIST_LOG(maphist,
" out of RAM breaking loan, waiting",
0,0,0,0);
cpu_count(CPU_COUNT_FLTNORAM, 1);
uvm_wait("flt_noram4");
return ERESTART;
}
*rpg = pg;
*ruobjpage = pg;
uvm_pagelock(pg);
uvm_pagewakeup(pg);
uvm_pageunlock(pg);
pg->flags &= ~PG_BUSY;
UVM_PAGE_OWN(pg, NULL);
}
return 0;
}
int
uvm_fault_lower_promote(
struct uvm_faultinfo *ufi, struct uvm_faultctx *flt,
struct uvm_object *uobj, struct vm_page *uobjpage)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
struct vm_anon *anon;
struct vm_page *pg;
int error;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(amap != NULL);
error = uvm_fault_upper_upgrade(ufi, flt, amap, uobj);
if (error != 0) {
return error;
}
KASSERT(rw_write_held(amap->am_lock));
KASSERT(uobj == NULL ||
rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
error = uvmfault_promote(ufi, NULL, uobjpage, &anon, &flt->anon_spare);
switch (error) {
case 0:
break;
case ERESTART:
return ERESTART;
default:
return error;
}
pg = anon->an_page;
if (uobjpage != PGO_DONTCARE) {
cpu_count(CPU_COUNT_FLT_PRCOPY, 1);
if ((amap_flags(amap) & AMAP_SHARED) != 0) {
pmap_page_protect(uobjpage, VM_PROT_NONE);
}
UVMHIST_LOG(maphist,
" promote uobjpage %#jx to anon/page %#jx/%#jx",
(uintptr_t)uobjpage, (uintptr_t)anon, (uintptr_t)pg, 0);
} else {
cpu_count(CPU_COUNT_FLT_PRZERO, 1);
UVMHIST_LOG(maphist," zero fill anon/page %#jx/%#jx",
(uintptr_t)anon, (uintptr_t)pg, 0, 0);
}
return uvm_fault_lower_enter(ufi, flt, uobj, anon, pg);
}
int
uvm_fault_lower_enter(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
struct uvm_object *uobj,
struct vm_anon *anon, struct vm_page *pg)
{
struct vm_amap * const amap = ufi->entry->aref.ar_amap;
const bool readonly = uvm_pagereadonly_p(pg);
int error;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
KASSERT(amap == NULL ||
rw_lock_op(amap->am_lock) == flt->upper_lock_type);
KASSERT(uobj == NULL ||
rw_lock_op(uobj->vmobjlock) == flt->lower_lock_type);
KASSERT(anon == NULL || anon->an_lock == amap->am_lock);
KASSERT((pg->flags & PG_RELEASED) == 0);
KASSERT((pg->flags & PG_BUSY) == 0);
UVMHIST_LOG(maphist,
" MAPPING: case2: pm=%#jx, va=%#jx, pg=%#jx, promote=%jd",
(uintptr_t)ufi->orig_map->pmap, ufi->orig_rvaddr,
(uintptr_t)pg, flt->promote);
KASSERTMSG((flt->access_type & VM_PROT_WRITE) == 0 || !readonly,
"promote=%u cow_now=%u access_type=%x enter_prot=%x cow=%u "
"entry=%p map=%p orig_rvaddr=%p pg=%p",
flt->promote, flt->cow_now, flt->access_type, flt->enter_prot,
UVM_ET_ISCOPYONWRITE(ufi->entry), ufi->entry, ufi->orig_map,
(void *)ufi->orig_rvaddr, pg);
KASSERT((flt->access_type & VM_PROT_WRITE) == 0 || !readonly);
if (pmap_enter(ufi->orig_map->pmap, ufi->orig_rvaddr,
VM_PAGE_TO_PHYS(pg),
readonly ? flt->enter_prot & ~VM_PROT_WRITE : flt->enter_prot,
flt->access_type | PMAP_CANFAIL |
(flt->wire_mapping ? PMAP_WIRED : 0)) != 0) {
if (anon != NULL) {
uvm_pagelock(pg);
uvm_pageenqueue(pg);
uvm_pagewakeup(pg);
uvm_pageunlock(pg);
}
uvmfault_unlockall(ufi, amap, uobj);
if (!uvm_reclaimable()) {
UVMHIST_LOG(maphist,
"<- failed. out of VM",0,0,0,0);
error = ENOMEM;
return error;
}
uvm_wait("flt_pmfail2");
return ERESTART;
}
uvm_fault_lower_done(ufi, flt, uobj, pg);
pmap_update(ufi->orig_map->pmap);
uvmfault_unlockall(ufi, amap, uobj);
UVMHIST_LOG(maphist, "<- done (SUCCESS!)",0,0,0,0);
return 0;
}
void
uvm_fault_lower_done(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
struct uvm_object *uobj, struct vm_page *pg)
{
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
if (flt->wire_paging) {
uvm_pagelock(pg);
uvm_pagewire(pg);
uvm_pageunlock(pg);
if (pg->flags & PG_AOBJ) {
KASSERT(uobj != NULL);
KASSERT(uobj->vmobjlock == pg->uobject->vmobjlock);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
uao_dropswap(pg->uobject, pg->offset >> PAGE_SHIFT);
}
} else if (uvmpdpol_pageactivate_p(pg)) {
uvm_pagelock(pg);
uvm_pageactivate(pg);
uvm_pageunlock(pg);
}
}
int
uvm_fault_wire(struct vm_map *map, vaddr_t start, vaddr_t end,
vm_prot_t access_type, int maxprot)
{
vaddr_t va;
int error;
if (start > end) {
return EFAULT;
}
for (va = start; va < end; va += PAGE_SIZE) {
error = uvm_fault_internal(map, va, access_type,
(maxprot ? UVM_FAULT_MAXPROT : 0) | UVM_FAULT_WIRE);
if (error) {
if (va != start) {
uvm_fault_unwire(map, start, va);
}
return error;
}
}
return 0;
}
void
uvm_fault_unwire(struct vm_map *map, vaddr_t start, vaddr_t end)
{
vm_map_lock_read(map);
uvm_fault_unwire_locked(map, start, end);
vm_map_unlock_read(map);
}
void
uvm_fault_unwire_locked(struct vm_map *map, vaddr_t start, vaddr_t end)
{
struct vm_map_entry *entry, *oentry;
pmap_t pmap = vm_map_pmap(map);
vaddr_t va;
paddr_t pa;
struct vm_page *pg;
KASSERT(start >= vm_map_min(map));
KASSERT(end <= vm_map_max(map));
if (uvm_map_lookup_entry(map, start, &entry) == false)
panic("uvm_fault_unwire_locked: address not in map");
oentry = NULL;
for (va = start; va < end; va += PAGE_SIZE) {
KASSERT(va >= entry->start);
while (va >= entry->end) {
KASSERT(entry->next != &map->header);
KASSERT(entry->next->start <= entry->end);
entry = entry->next;
}
if (entry != oentry) {
if (oentry != NULL) {
uvm_map_unlock_entry(oentry);
}
uvm_map_lock_entry(entry, RW_WRITER);
oentry = entry;
}
if (!pmap_extract(pmap, va, &pa))
continue;
if (VM_MAPENT_ISWIRED(entry) == 0)
pmap_unwire(pmap, va);
pg = PHYS_TO_VM_PAGE(pa);
if (pg) {
uvm_pagelock(pg);
uvm_pageunwire(pg);
uvm_pageunlock(pg);
}
}
if (oentry != NULL) {
uvm_map_unlock_entry(entry);
}
}