Merge from yamt-pagecache (after much testing):

- Reduce unnecessary page scan in putpages esp. when an object has a ton of
  pages cached but only a few of them are dirty.

- Reduce the number of pmap operations by tracking page dirtiness more
  precisely in uvm layer.
This commit is contained in:
ad 2020-01-15 17:55:43 +00:00
parent 5425c6e737
commit 05a3457e85
34 changed files with 885 additions and 357 deletions

View File

@ -746,7 +746,8 @@ mappedread(vnode_t *vp, int nbytes, uio_t *uio)
pp = NULL;
npages = 1;
mutex_enter(mtx);
found = uvn_findpages(uobj, start, &npages, &pp, UFP_NOALLOC);
found = uvn_findpages(uobj, start, &npages, &pp, NULL,
UFP_NOALLOC);
mutex_exit(mtx);
/* XXXNETBSD shouldn't access userspace with the page busy */
@ -792,7 +793,8 @@ update_pages(vnode_t *vp, int64_t start, int len, objset_t *os, uint64_t oid,
pp = NULL;
npages = 1;
found = uvn_findpages(uobj, start, &npages, &pp, UFP_NOALLOC);
found = uvn_findpages(uobj, start, &npages, &pp, NULL,
UFP_NOALLOC);
if (found) {
mutex_exit(mtx);
@ -5976,7 +5978,7 @@ zfs_netbsd_getpages(void *v)
}
npages = 1;
pg = NULL;
uvn_findpages(uobj, offset, &npages, &pg, UFP_ALL);
uvn_findpages(uobj, offset, &npages, &pg, NULL, UFP_ALL);
if (pg->flags & PG_FAKE) {
mutex_exit(mtx);
@ -6224,7 +6226,7 @@ zfs_netbsd_setsize(vnode_t *vp, off_t size)
mutex_enter(mtx);
count = 1;
pg = NULL;
if (uvn_findpages(uobj, tsize, &count, &pg, UFP_NOALLOC)) {
if (uvn_findpages(uobj, tsize, &count, &pg, NULL, UFP_NOALLOC)) {
va = zfs_map_page(pg, S_WRITE);
pgoff = size - tsize;
memset(va + pgoff, 0, PAGESIZE - pgoff);

View File

@ -1,4 +1,4 @@
/* $NetBSD: drm_gem.c,v 1.10 2018/08/27 15:22:53 riastradh Exp $ */
/* $NetBSD: drm_gem.c,v 1.11 2020/01/15 17:55:43 ad Exp $ */
/*
* Copyright © 2008 Intel Corporation
@ -28,7 +28,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: drm_gem.c,v 1.10 2018/08/27 15:22:53 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: drm_gem.c,v 1.11 2020/01/15 17:55:43 ad Exp $");
#include <linux/types.h>
#include <linux/slab.h>
@ -612,8 +612,10 @@ drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, bool dirty,
unsigned i;
for (i = 0; i < (obj->size >> PAGE_SHIFT); i++) {
if (dirty)
pages[i]->p_vmp.flags &= ~PG_CLEAN;
if (dirty) {
uvm_pagemarkdirty(&pages[i]->p_vmp,
UVM_PAGE_STATUS_DIRTY);
}
}
uvm_obj_unwirepages(obj->filp, 0, obj->size);

View File

@ -1,4 +1,4 @@
/* $NetBSD: i915_gem.c,v 1.54 2018/08/27 15:22:54 riastradh Exp $ */
/* $NetBSD: i915_gem.c,v 1.55 2020/01/15 17:55:43 ad Exp $ */
/*
* Copyright © 2008-2015 Intel Corporation
@ -28,7 +28,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: i915_gem.c,v 1.54 2018/08/27 15:22:54 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: i915_gem.c,v 1.55 2020/01/15 17:55:43 ad Exp $");
#ifdef __NetBSD__
#if 0 /* XXX uvmhist option? */
@ -2644,7 +2644,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
if (obj->dirty) {
TAILQ_FOREACH(page, &obj->pageq, pageq.queue) {
page->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(page, UVM_PAGE_STATUS_DIRTY);
/* XXX mark page accessed */
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: i915_gem_fence.c,v 1.5 2018/08/27 15:09:35 riastradh Exp $ */
/* $NetBSD: i915_gem_fence.c,v 1.6 2020/01/15 17:55:43 ad Exp $ */
/*
* Copyright © 2008-2015 Intel Corporation
@ -24,7 +24,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: i915_gem_fence.c,v 1.5 2018/08/27 15:09:35 riastradh Exp $");
__KERNEL_RCSID(0, "$NetBSD: i915_gem_fence.c,v 1.6 2020/01/15 17:55:43 ad Exp $");
#include <drm/drmP.h>
#include <drm/i915_drm.h>
@ -769,7 +769,7 @@ i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
(test_bit(i, obj->bit_17) != 0)) {
i915_gem_swizzle_page(container_of(page, struct page,
p_vmp));
page->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(page, UVM_PAGE_STATUS_DIRTY);
}
i += 1;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: mm.h,v 1.9 2018/08/27 13:44:54 riastradh Exp $ */
/* $NetBSD: mm.h,v 1.10 2020/01/15 17:55:44 ad Exp $ */
/*-
* Copyright (c) 2013 The NetBSD Foundation, Inc.
@ -96,7 +96,7 @@ static inline void
set_page_dirty(struct page *page)
{
page->p_vmp.flags &= ~PG_CLEAN;
uvm_pagemarkdirty(&page->p_vmp, UVM_PAGE_STATUS_DIRTY);
}
#endif /* _LINUX_MM_H_ */

View File

@ -1,4 +1,4 @@
/* $NetBSD: genfs_io.c,v 1.83 2019/12/31 22:42:50 ad Exp $ */
/* $NetBSD: genfs_io.c,v 1.84 2020/01/15 17:55:44 ad Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1993
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: genfs_io.c,v 1.83 2019/12/31 22:42:50 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: genfs_io.c,v 1.84 2020/01/15 17:55:44 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -86,10 +86,8 @@ genfs_rel_pages(struct vm_page **pgs, unsigned int npages)
static void
genfs_markdirty(struct vnode *vp)
{
struct genfs_node * const gp = VTOG(vp);
KASSERT(mutex_owned(vp->v_interlock));
gp->g_dirtygen++;
if ((vp->v_iflag & VI_ONWORKLST) == 0) {
vn_syncer_add_to_worklist(vp, filedelay);
}
@ -137,6 +135,7 @@ genfs_getpages(void *v)
UVMHIST_LOG(ubchist, "vp %#jx off 0x%jx/%jx count %jd",
(uintptr_t)vp, ap->a_offset >> 32, ap->a_offset, *ap->a_count);
KASSERT(memwrite >= overwrite);
KASSERT(vp->v_type == VREG || vp->v_type == VDIR ||
vp->v_type == VLNK || vp->v_type == VBLK);
@ -231,12 +230,17 @@ startover:
}
#endif /* defined(DEBUG) */
nfound = uvn_findpages(uobj, origoffset, &npages,
ap->a_m, UFP_NOWAIT|UFP_NOALLOC|(memwrite ? UFP_NORDONLY : 0));
ap->a_m, NULL,
UFP_NOWAIT|UFP_NOALLOC|(memwrite ? UFP_NORDONLY : 0));
KASSERT(npages == *ap->a_count);
if (nfound == 0) {
error = EBUSY;
goto out_err;
}
/*
* lock and unlock g_glock to ensure that no one is truncating
* the file behind us.
*/
if (!genfs_node_rdtrylock(vp)) {
genfs_rel_pages(ap->a_m, npages);
@ -258,6 +262,17 @@ startover:
}
error = (ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0);
if (error == 0 && memwrite) {
for (i = 0; i < npages; i++) {
pg = ap->a_m[i];
if (pg == NULL || pg == PGO_DONTCARE) {
continue;
}
if (uvm_pagegetdirty(pg) ==
UVM_PAGE_STATUS_CLEAN) {
uvm_pagemarkdirty(pg,
UVM_PAGE_STATUS_UNKNOWN);
}
}
genfs_markdirty(vp);
}
goto out_err;
@ -351,7 +366,7 @@ startover:
goto startover;
}
if (uvn_findpages(uobj, origoffset, &npages, &pgs[ridx],
if (uvn_findpages(uobj, origoffset, &npages, &pgs[ridx], NULL,
async ? UFP_NOWAIT : UFP_ALL) != orignmempages) {
if (!glocked) {
genfs_node_unlock(vp);
@ -363,27 +378,6 @@ startover:
goto out_err_free;
}
/*
* if the pages are already resident, just return them.
*/
for (i = 0; i < npages; i++) {
struct vm_page *pg = pgs[ridx + i];
if ((pg->flags & PG_FAKE) ||
(blockalloc && (pg->flags & PG_RDONLY))) {
break;
}
}
if (i == npages) {
if (!glocked) {
genfs_node_unlock(vp);
}
UVMHIST_LOG(ubchist, "returning cached pages", 0,0,0,0);
npages += ridx;
goto out;
}
/*
* if PGO_OVERWRITE is set, don't bother reading the pages.
*/
@ -397,12 +391,49 @@ startover:
for (i = 0; i < npages; i++) {
struct vm_page *pg = pgs[ridx + i];
pg->flags &= ~(PG_RDONLY|PG_CLEAN);
/*
* it's caller's responsibility to allocate blocks
* beforehand for the overwrite case.
*/
KASSERT((pg->flags & PG_RDONLY) == 0 || !blockalloc);
pg->flags &= ~PG_RDONLY;
/*
* mark the page DIRTY.
* otherwise another thread can do putpages and pull
* our vnode from syncer's queue before our caller does
* ubc_release. note that putpages won't see CLEAN
* pages even if they are BUSY.
*/
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
}
npages += ridx;
goto out;
}
/*
* if the pages are already resident, just return them.
*/
for (i = 0; i < npages; i++) {
struct vm_page *pg = pgs[ridx + i];
if ((pg->flags & PG_FAKE) ||
(blockalloc && (pg->flags & PG_RDONLY) != 0)) {
break;
}
}
if (i == npages) {
if (!glocked) {
genfs_node_unlock(vp);
}
UVMHIST_LOG(ubchist, "returning cached pages", 0,0,0,0);
npages += ridx;
goto out;
}
/*
* the page wasn't resident and we're not overwriting,
* so we're going to have to do some i/o.
@ -425,7 +456,7 @@ startover:
UVMHIST_LOG(ubchist, "reset npages start 0x%jx end 0x%jx",
startoffset, endoffset, 0,0);
npgs = npages;
if (uvn_findpages(uobj, startoffset, &npgs, pgs,
if (uvn_findpages(uobj, startoffset, &npgs, pgs, NULL,
async ? UFP_NOWAIT : UFP_ALL) != npages) {
if (!glocked) {
genfs_node_unlock(vp);
@ -473,8 +504,16 @@ out:
UVMHIST_LOG(ubchist, "examining pg %#jx flags 0x%jx",
(uintptr_t)pg, pg->flags, 0,0);
if (pg->flags & PG_FAKE && !overwrite) {
pg->flags &= ~(PG_FAKE);
pmap_clear_modify(pgs[i]);
/*
* we've read page's contents from the backing storage.
*
* for a read fault, we keep them CLEAN; if we
* encountered a hole while reading, the pages can
* already been dirtied with zeros.
*/
KASSERTMSG(blockalloc || uvm_pagegetdirty(pg) ==
UVM_PAGE_STATUS_CLEAN, "page %p not clean", pg);
pg->flags &= ~PG_FAKE;
}
KASSERT(!memwrite || !blockalloc || (pg->flags & PG_RDONLY) == 0);
if (i < ridx || i >= ridx + orignmempages || async) {
@ -496,6 +535,13 @@ out:
uvm_pageunlock(pg);
pg->flags &= ~(PG_WANTED|PG_BUSY|PG_FAKE);
UVM_PAGE_OWN(pg, NULL);
} else if (memwrite && !overwrite &&
uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_CLEAN) {
/*
* for a write fault, start dirtiness tracking of
* requested pages.
*/
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_UNKNOWN);
}
}
if (memwrite) {
@ -690,16 +736,13 @@ genfs_getpages_read(struct vnode *vp, struct vm_page **pgs, int npages,
iobytes);
skipbytes += iobytes;
mutex_enter(uobj->vmobjlock);
for (i = 0; i < holepages; i++) {
if (memwrite) {
pgs[pidx + i]->flags &= ~PG_CLEAN;
}
if (!blockalloc) {
if (!blockalloc) {
mutex_enter(uobj->vmobjlock);
for (i = 0; i < holepages; i++) {
pgs[pidx + i]->flags |= PG_RDONLY;
}
mutex_exit(uobj->vmobjlock);
}
mutex_exit(uobj->vmobjlock);
continue;
}
@ -764,7 +807,8 @@ loopdone:
if (pg == NULL) {
continue;
}
pg->flags &= ~(PG_CLEAN|PG_RDONLY);
pg->flags &= ~PG_RDONLY;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
UVMHIST_LOG(ubchist, "mark dirty pg %#jx",
(uintptr_t)pg, 0, 0, 0);
}
@ -793,11 +837,11 @@ loopdone:
* this routine is holding the lock on the object. the only time
* that it can run into a PG_BUSY page that it does not own is if
* some other process has started I/O on the page (e.g. either
* a pagein, or a pageout). if the PG_BUSY page is being paged
* in, then it can not be dirty (!PG_CLEAN) because no one has
* had a chance to modify it yet. if the PG_BUSY page is being
* paged out then it means that someone else has already started
* cleaning the page for us (how nice!). in this case, if we
* a pagein, or a pageout). if the PG_BUSY page is being paged
* in, then it can not be dirty (!UVM_PAGE_STATUS_CLEAN) because no
* one has had a chance to modify it yet. if the PG_BUSY page is
* being paged out then it means that someone else has already started
* cleaning the page for us (how nice!). in this case, if we
* have syncio specified, then after we make our pass through the
* object we need to wait for the other PG_BUSY pages to clear
* off (i.e. we need to do an iosync). also note that once a
@ -839,14 +883,13 @@ genfs_do_putpages(struct vnode *vp, off_t startoff, off_t endoff,
bool async = (origflags & PGO_SYNCIO) == 0;
bool pagedaemon = curlwp == uvm.pagedaemon_lwp;
struct lwp * const l = curlwp ? curlwp : &lwp0;
struct genfs_node * const gp = VTOG(vp);
struct mount *trans_mp;
int flags;
int dirtygen;
bool modified;
bool modified; /* if we write out any pages */
bool holds_wapbl;
bool cleanall;
bool cleanall; /* try to pull off from the syncer's list */
bool onworklst;
const bool dirtyonly = (origflags & (PGO_DEACTIVATE|PGO_FREE)) == 0;
UVMHIST_FUNC("genfs_putpages"); UVMHIST_CALLED(ubchist);
@ -870,7 +913,14 @@ retry:
flags = origflags;
KASSERT((vp->v_iflag & VI_ONWORKLST) != 0 ||
(vp->v_iflag & VI_WRMAPDIRTY) == 0);
if (uobj->uo_npages == 0) {
/*
* shortcut if we have no pages to process.
*/
if (uobj->uo_npages == 0 || (dirtyonly &&
radix_tree_empty_tagged_tree_p(&uobj->uo_pages,
UVM_PAGE_DIRTY_TAG))) {
if (vp->v_iflag & VI_ONWORKLST) {
vp->v_iflag &= ~VI_WRMAPDIRTY;
if (LIST_FIRST(&vp->v_dirtyblkhd) == NULL)
@ -940,7 +990,7 @@ retry:
if ((vp->v_iflag & VI_ONWORKLST) == 0) {
#if !defined(DEBUG)
if ((flags & (PGO_FREE|PGO_DEACTIVATE)) == 0) {
if (dirtyonly) {
goto skip_scan;
}
#endif /* !defined(DEBUG) */
@ -951,18 +1001,23 @@ retry:
* start the loop to scan pages.
*/
cleanall = (flags & PGO_CLEANIT) != 0 && wasclean &&
startoff == 0 && endoff == trunc_page(LLONG_MAX) &&
(vp->v_iflag & VI_ONWORKLST) != 0;
dirtygen = gp->g_dirtygen;
cleanall = true;
freeflag = pagedaemon ? PG_PAGEOUT : PG_RELEASED;
uvm_page_array_init(&a);
for (;;) {
bool pgprotected;
/*
* if the current page is not interesting, move on to the next.
* if !dirtyonly, iterate over all resident pages in the range.
*
* if dirtyonly, only possibly dirty pages are interesting.
* however, if we are asked to sync for integrity, we should
* wait on pages being written back by other threads as well.
*/
pg = uvm_page_array_fill_and_peek(&a, uobj, nextoff, 0, 0);
pg = uvm_page_array_fill_and_peek(&a, uobj, nextoff, 0,
dirtyonly ? (UVM_PAGE_ARRAY_FILL_DIRTY |
(!async ? UVM_PAGE_ARRAY_FILL_WRITEBACK : 0)) : 0);
if (pg == NULL) {
break;
}
@ -972,18 +1027,15 @@ retry:
(pg->flags & (PG_BUSY)) != 0);
KASSERT(pg->offset >= startoff);
KASSERT(pg->offset >= nextoff);
KASSERT(!dirtyonly ||
uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN ||
radix_tree_get_tag(&uobj->uo_pages,
pg->offset >> PAGE_SHIFT, UVM_PAGE_WRITEBACK_TAG));
if (pg->offset >= endoff) {
break;
}
if (pg->flags & (PG_RELEASED|PG_PAGEOUT)) {
wasclean = false;
nextoff = pg->offset + PAGE_SIZE;
uvm_page_array_advance(&a);
continue;
}
/*
* a preempt point.
*/
@ -1003,14 +1055,14 @@ retry:
}
/*
* if the current page needs to be cleaned and it's busy,
* wait for it to become unbusy.
* if the current page is busy, wait for it to become unbusy.
*/
if (pg->flags & PG_BUSY) {
if ((pg->flags & PG_BUSY) != 0) {
UVMHIST_LOG(ubchist, "busy %#jx", (uintptr_t)pg,
0, 0, 0);
if (flags & PGO_BUSYFAIL && pg->flags & PG_BUSY) {
if ((pg->flags & (PG_RELEASED|PG_PAGEOUT)) != 0
&& (flags & PGO_BUSYFAIL) != 0) {
UVMHIST_LOG(ubchist, "busyfail %#jx",
(uintptr_t)pg, 0, 0, 0);
error = EDEADLK;
@ -1025,6 +1077,16 @@ retry:
*/
break;
}
/*
* don't bother to wait on other's activities
* unless we are asked to sync for integrity.
*/
if (!async && (flags & PGO_RECLAIM) == 0) {
wasclean = false;
nextoff = pg->offset + PAGE_SIZE;
uvm_page_array_advance(&a);
continue;
}
nextoff = pg->offset; /* visit this page again */
pg->flags |= PG_WANTED;
UVM_UNLOCK_AND_WAIT(pg, slock, 0, "genput", 0);
@ -1045,8 +1107,10 @@ retry:
* if we're cleaning, check if the page is needs to be cleaned.
*/
pgprotected = false;
if (flags & PGO_FREE) {
pmap_page_protect(pg, VM_PROT_NONE);
pgprotected = true;
} else if (flags & PGO_CLEANIT) {
/*
@ -1054,8 +1118,7 @@ retry:
* from the syncer queue, write-protect the page.
*/
if (cleanall && wasclean &&
gp->g_dirtygen == dirtygen) {
if (cleanall && wasclean) {
/*
* uobj pages get wired only by uvm_fault
@ -1065,6 +1128,7 @@ retry:
if (pg->wire_count == 0) {
pmap_page_protect(pg,
VM_PROT_READ|VM_PROT_EXECUTE);
pgprotected = true;
} else {
cleanall = false;
}
@ -1072,17 +1136,14 @@ retry:
}
if (flags & PGO_CLEANIT) {
needs_clean = pmap_clear_modify(pg) ||
(pg->flags & PG_CLEAN) == 0;
pg->flags |= PG_CLEAN;
needs_clean = uvm_pagecheckdirty(pg, pgprotected);
} else {
needs_clean = false;
}
/*
* if we're cleaning, build a cluster.
* the cluster will consist of pages which are currently dirty,
* but they will be returned to us marked clean.
* the cluster will consist of pages which are currently dirty.
* if not cleaning, just operate on the one page.
*/
@ -1118,7 +1179,8 @@ retry:
npages = (off - lo) >> PAGE_SHIFT;
nback = npages;
uvn_findpages(uobj, off - PAGE_SIZE, &nback, &pgs[0],
uvn_findpages(uobj, off - PAGE_SIZE, &nback,
&pgs[0], NULL,
UFP_NOWAIT|UFP_NOALLOC|UFP_DIRTYONLY|UFP_BACKWARD);
if (nback) {
memmove(&pgs[0], &pgs[npages - nback],
@ -1140,6 +1202,14 @@ retry:
/*
* then look forward to fill in the remaining space in
* the array of pages.
*
* pass our cached array of pages so that hopefully
* uvn_findpages can find some good pages in it.
* the array a was filled above with the one of
* following sets of flags:
* 0
* UVM_PAGE_ARRAY_FILL_DIRTY
* UVM_PAGE_ARRAY_FILL_DIRTY|WRITEBACK
*/
npages = MAXPAGES - nback - 1;
@ -1147,7 +1217,7 @@ retry:
npages = MIN(npages,
(fshi - off - 1) >> PAGE_SHIFT);
uvn_findpages(uobj, off + PAGE_SIZE, &npages,
&pgs[nback + 1],
&pgs[nback + 1], NULL,
UFP_NOWAIT|UFP_NOALLOC|UFP_DIRTYONLY);
npages += nback + 1;
} else {
@ -1163,6 +1233,19 @@ retry:
for (i = 0; i < npages; i++) {
tpg = pgs[i];
KASSERT(tpg->uobject == uobj);
KASSERT(i == 0 ||
pgs[i-1]->offset + PAGE_SIZE == tpg->offset);
KASSERT(!needs_clean || uvm_pagegetdirty(pgs[i]) !=
UVM_PAGE_STATUS_DIRTY);
if (needs_clean) {
/*
* mark pages as WRITEBACK so that concurrent
* fsync can find and wait for our activities.
*/
radix_tree_set_tag(&uobj->uo_pages,
pgs[i]->offset >> PAGE_SHIFT,
UVM_PAGE_WRITEBACK_TAG);
}
if (tpg->offset < startoff || tpg->offset >= endoff)
continue;
if (flags & PGO_DEACTIVATE && tpg->wire_count == 0) {
@ -1224,6 +1307,16 @@ retry:
}
uvm_page_array_fini(&a);
/*
* update ctime/mtime if the modification we started writing out might
* be from mmap'ed write.
*
* this is necessary when an application keeps a file mmaped and
* repeatedly modifies it via the window. note that, because we
* don't always write-protect pages when cleaning, such modifications
* might not involve any page faults.
*/
if (modified && (vp->v_iflag & VI_WRMAPDIRTY) != 0 &&
(vp->v_type != VBLK ||
(vp->v_mount->mnt_flag & MNT_NODEVMTIME) == 0)) {
@ -1231,34 +1324,13 @@ retry:
}
/*
* if we're cleaning and there was nothing to clean,
* take us off the syncer list. if we started any i/o
* and we're doing sync i/o, wait for all writes to finish.
* if we no longer have any possibly dirty pages, take us off the
* syncer list.
*/
if (cleanall && wasclean && gp->g_dirtygen == dirtygen &&
(vp->v_iflag & VI_ONWORKLST) != 0) {
#if defined(DEBUG)
uvm_page_array_init(&a);
for (nextoff = 0;; nextoff = pg->offset + PAGE_SIZE) {
pg = uvm_page_array_fill_and_peek(&a, uobj, nextoff,
0, 0);
if (pg == NULL) {
break;
}
uvm_page_array_advance(&a);
if ((pg->flags & (PG_FAKE | PG_MARKER)) != 0) {
continue;
}
if ((pg->flags & PG_CLEAN) == 0) {
printf("%s: %p: !CLEAN\n", __func__, pg);
}
if (pmap_is_modified(pg)) {
printf("%s: %p: modified\n", __func__, pg);
}
}
uvm_page_array_fini(&a);
#endif /* defined(DEBUG) */
if ((vp->v_iflag & VI_ONWORKLST) != 0 &&
radix_tree_empty_tagged_tree_p(&uobj->uo_pages,
UVM_PAGE_DIRTY_TAG)) {
vp->v_iflag &= ~VI_WRMAPDIRTY;
if (LIST_FIRST(&vp->v_dirtyblkhd) == NULL)
vn_syncer_remove_from_worklist(vp);
@ -1557,7 +1629,7 @@ genfs_compat_getpages(void *v)
pgs = ap->a_m;
if (ap->a_flags & PGO_LOCKED) {
uvn_findpages(uobj, origoffset, ap->a_count, ap->a_m,
uvn_findpages(uobj, origoffset, ap->a_count, ap->a_m, NULL,
UFP_NOWAIT|UFP_NOALLOC| (memwrite ? UFP_NORDONLY : 0));
error = ap->a_m[ap->a_centeridx] == NULL ? EBUSY : 0;
@ -1575,7 +1647,7 @@ genfs_compat_getpages(void *v)
return 0;
}
npages = orignpages;
uvn_findpages(uobj, origoffset, &npages, pgs, UFP_ALL);
uvn_findpages(uobj, origoffset, &npages, pgs, NULL, UFP_ALL);
mutex_exit(uobj->vmobjlock);
kva = uvm_pagermapin(pgs, npages,
UVMPAGER_MAPIN_READ | UVMPAGER_MAPIN_WAITOK);
@ -1608,7 +1680,7 @@ genfs_compat_getpages(void *v)
if (error && (pg->flags & PG_FAKE) != 0) {
pg->flags |= PG_RELEASED;
} else {
pmap_clear_modify(pg);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_UNKNOWN);
uvm_pagelock(pg);
uvm_pageactivate(pg);
uvm_pageunlock(pg);

View File

@ -1,4 +1,4 @@
/* $NetBSD: genfs_node.h,v 1.22 2018/05/28 21:04:38 chs Exp $ */
/* $NetBSD: genfs_node.h,v 1.23 2020/01/15 17:55:44 ad Exp $ */
/*
* Copyright (c) 2001 Chuck Silvers.
@ -80,7 +80,6 @@ struct genfs_ops {
struct genfs_node {
const struct genfs_ops *g_op; /* ops vector */
krwlock_t g_glock; /* getpages lock */
int g_dirtygen;
};
#define VTOG(vp) ((struct genfs_node *)(vp)->v_data)

View File

@ -1,4 +1,4 @@
/* $NetBSD: nfs_bio.c,v 1.192 2019/12/13 20:10:21 ad Exp $ */
/* $NetBSD: nfs_bio.c,v 1.193 2020/01/15 17:55:44 ad Exp $ */
/*
* Copyright (c) 1989, 1993
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.192 2019/12/13 20:10:21 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.193 2020/01/15 17:55:44 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_nfs.h"
@ -1120,7 +1120,8 @@ again:
*/
mutex_enter(uobj->vmobjlock);
for (i = 0; i < npages; i++) {
pgs[i]->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pgs[i],
UVM_PAGE_STATUS_DIRTY);
}
mutex_exit(uobj->vmobjlock);
}

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile.rumpkern,v 1.181 2019/12/20 21:20:09 ad Exp $
# $NetBSD: Makefile.rumpkern,v 1.182 2020/01/15 17:55:44 ad Exp $
#
IOCONFDIR:= ${.PARSEDIR}
@ -139,7 +139,7 @@ SRCS+= init_sysctl_base.c \
# sys/uvm
SRCS+= uvm_aobj.c uvm_readahead.c uvm_object.c uvm_swapstub.c
SRCS+= uvm_page_array.c
SRCS+= uvm_page_array.c uvm_page_status.c
# 4.4BSD secmodel. selection is hardcoded for now
SRCS+= secmodel.c

View File

@ -1,4 +1,4 @@
/* $NetBSD: vm.c,v 1.182 2020/01/05 15:57:15 para Exp $ */
/* $NetBSD: vm.c,v 1.183 2020/01/15 17:55:44 ad Exp $ */
/*
* Copyright (c) 2007-2011 Antti Kantee. All Rights Reserved.
@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vm.c,v 1.182 2020/01/05 15:57:15 para Exp $");
__KERNEL_RCSID(0, "$NetBSD: vm.c,v 1.183 2020/01/15 17:55:44 ad Exp $");
#include <sys/param.h>
#include <sys/atomic.h>
@ -235,7 +235,7 @@ void
uvm_pagezero(struct vm_page *pg)
{
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
memset((void *)pg->uanon, 0, PAGE_SIZE);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: vm_vfs.c,v 1.35 2019/12/13 20:10:22 ad Exp $ */
/* $NetBSD: vm_vfs.c,v 1.36 2020/01/15 17:55:44 ad Exp $ */
/*
* Copyright (c) 2008-2011 Antti Kantee. All Rights Reserved.
@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: vm_vfs.c,v 1.35 2019/12/13 20:10:22 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: vm_vfs.c,v 1.36 2020/01/15 17:55:44 ad Exp $");
#include <sys/param.h>
@ -141,7 +141,7 @@ ubc_zerorange(struct uvm_object *uobj, off_t off, size_t len, int flags)
start = (uint8_t *)pg->uanon + chunkoff;
memset(start, 0, chunklen);
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
off += chunklen;
len -= chunklen;
@ -210,8 +210,10 @@ ubc_uiomove(struct uvm_object *uobj, struct uio *uio, vsize_t todo,
mutex_exit(uobj->vmobjlock);
goto out;
}
if (uio->uio_rw == UIO_WRITE)
pg->flags &= ~(PG_CLEAN | PG_FAKE);
if (uio->uio_rw == UIO_WRITE) {
pg->flags &= ~PG_FAKE;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
}
todo -= xfersize;
}
uvm_page_unbusy(pgs, npages);

View File

@ -1,4 +1,4 @@
/* $NetBSD: cpu_data.h,v 1.48 2020/01/12 13:29:24 ad Exp $ */
/* $NetBSD: cpu_data.h,v 1.49 2020/01/15 17:55:44 ad Exp $ */
/*-
* Copyright (c) 2004, 2006, 2007, 2008, 2019 The NetBSD Foundation, Inc.
@ -87,7 +87,15 @@ enum cpu_count {
CPU_COUNT_FLTNOANON,
CPU_COUNT_FLTNORAM,
CPU_COUNT_FLTPGRELE,
CPU_COUNT_MAX /* 40 */
CPU_COUNT_ANONUNKNOWN, /* 40 */
CPU_COUNT_ANONCLEAN,
CPU_COUNT_ANONDIRTY,
CPU_COUNT_FILEUNKNOWN,
CPU_COUNT_FILECLEAN,
CPU_COUNT_FILEDIRTY,
CPU_COUNT__UNUSED1,
CPU_COUNT__UNUSED2,
CPU_COUNT_MAX /* 48 */
};
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: lfs_pages.c,v 1.19 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: lfs_pages.c,v 1.20 2020/01/15 17:55:44 ad Exp $ */
/*-
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2019 The NetBSD Foundation, Inc.
@ -60,7 +60,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lfs_pages.c,v 1.19 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: lfs_pages.c,v 1.20 2020/01/15 17:55:44 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@ -306,8 +306,10 @@ check_dirty(struct lfs *fs, struct vnode *vp,
UVM_PAGE_OWN(pg, "lfs_putpages");
pmap_page_protect(pg, VM_PROT_NONE);
tdirty = (pmap_clear_modify(pg) ||
(pg->flags & PG_CLEAN) == 0);
tdirty =
uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN &&
(uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_DIRTY ||
pmap_clear_modify(pg));
dirty += tdirty;
}
if ((pages_per_block > 0 && nonexistent >= pages_per_block) ||
@ -329,10 +331,11 @@ check_dirty(struct lfs *fs, struct vnode *vp,
for (i = 0; i == 0 || i < pages_per_block; i++) {
KASSERT(mutex_owned(vp->v_interlock));
pg = pgs[i];
KASSERT(!((pg->flags & PG_CLEAN) && (pg->flags & PG_DELWRI)));
KASSERT(!(uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_DIRTY
&& (pg->flags & PG_DELWRI)));
KASSERT(pg->flags & PG_BUSY);
if (dirty) {
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
if (flags & PGO_FREE) {
/*
* Wire the page so that

View File

@ -1,4 +1,4 @@
/* $NetBSD: lfs_segment.c,v 1.280 2019/12/08 19:52:37 ad Exp $ */
/* $NetBSD: lfs_segment.c,v 1.281 2020/01/15 17:55:44 ad Exp $ */
/*-
* Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
@ -60,7 +60,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lfs_segment.c,v 1.280 2019/12/08 19:52:37 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: lfs_segment.c,v 1.281 2020/01/15 17:55:44 ad Exp $");
#ifdef DEBUG
# define vndebug(vp, str) do { \
@ -241,7 +241,8 @@ lfs_vflush(struct vnode *vp)
pg = uvm_pagelookup(&vp->v_uobj, off);
if (pg == NULL)
continue;
if ((pg->flags & PG_CLEAN) == 0 ||
if (uvm_pagegetdirty(pg)
== UVM_PAGE_STATUS_DIRTY ||
pmap_is_modified(pg)) {
lfs_sb_addavail(fs,
lfs_btofsb(fs,

View File

@ -1,4 +1,4 @@
/* $NetBSD: lfs_vfsops.c,v 1.367 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: lfs_vfsops.c,v 1.368 2020/01/15 17:55:44 ad Exp $ */
/*-
* Copyright (c) 1999, 2000, 2001, 2002, 2003, 2007, 2007
@ -61,7 +61,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: lfs_vfsops.c,v 1.367 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: lfs_vfsops.c,v 1.368 2020/01/15 17:55:44 ad Exp $");
#if defined(_KERNEL_OPT)
#include "opt_lfs.h"
@ -2249,7 +2249,8 @@ lfs_gop_write(struct vnode *vp, struct vm_page **pgs, int npages,
}
uvm_pageactivate(pg);
uvm_pageunlock(pg);
pg->flags &= ~(PG_CLEAN|PG_DELWRI|PG_PAGEOUT|PG_RELEASED);
pg->flags &= ~(PG_DELWRI|PG_PAGEOUT|PG_RELEASED);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
DLOG((DLOG_PAGE, "pg[%d] = %p (vp %p off %" PRIx64 ")\n", i, pg,
vp, pg->offset));
DLOG((DLOG_PAGE, "pg[%d]->flags = %x\n", i, pg->flags));

View File

@ -1,4 +1,4 @@
/* $NetBSD: ulfs_inode.c,v 1.23 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: ulfs_inode.c,v 1.24 2020/01/15 17:55:44 ad Exp $ */
/* from NetBSD: ufs_inode.c,v 1.95 2015/06/13 14:56:45 hannken Exp */
/*
@ -38,7 +38,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ulfs_inode.c,v 1.23 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: ulfs_inode.c,v 1.24 2020/01/15 17:55:44 ad Exp $");
#if defined(_KERNEL_OPT)
#include "opt_lfs.h"
@ -223,13 +223,13 @@ ulfs_balloc_range(struct vnode *vp, off_t off, off_t len, kauth_cred_t cred,
genfs_node_unlock(vp);
/*
* if the allocation succeeded, clear PG_CLEAN on all the pages
* and clear PG_RDONLY on any pages that are now fully backed
* by disk blocks. if the allocation failed, we do not invalidate
* the pages since they might have already existed and been dirty,
* in which case we need to keep them around. if we created the pages,
* they will be clean and read-only, and leaving such pages
* in the cache won't cause any problems.
* if the allocation succeeded, mark all pages dirty and clear
* PG_RDONLY on any pages that are now fully backed by disk blocks.
* if the allocation failed, we do not invalidate the pages since
* they might have already existed and been dirty, in which case we
* need to keep them around. if we created the pages, they will be
* clean and read-only, and leaving such pages in the cache won't
* cause any problems.
*/
GOP_SIZE(vp, off + len, &eob, 0);
@ -241,7 +241,7 @@ ulfs_balloc_range(struct vnode *vp, off_t off, off_t len, kauth_cred_t cred,
pagestart + ((i + 1) << PAGE_SHIFT) <= eob) {
pgs[i]->flags &= ~PG_RDONLY;
}
pgs[i]->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pgs[i], UVM_PAGE_STATUS_DIRTY);
}
uvm_pagelock(pgs[i]);
uvm_pageactivate(pgs[i]);

View File

@ -1,4 +1,4 @@
/* $NetBSD: ufs_inode.c,v 1.107 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: ufs_inode.c,v 1.108 2020/01/15 17:55:44 ad Exp $ */
/*
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ufs_inode.c,v 1.107 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: ufs_inode.c,v 1.108 2020/01/15 17:55:44 ad Exp $");
#if defined(_KERNEL_OPT)
#include "opt_ffs.h"
@ -259,7 +259,7 @@ ufs_balloc_range(struct vnode *vp, off_t off, off_t len, kauth_cred_t cred,
genfs_node_unlock(vp);
/*
* if the allocation succeeded, clear PG_CLEAN on all the pages
* if the allocation succeeded, mark all the pages dirty
* and clear PG_RDONLY on any pages that are now fully backed
* by disk blocks. if the allocation failed, we do not invalidate
* the pages since they might have already existed and been dirty,
@ -277,7 +277,7 @@ ufs_balloc_range(struct vnode *vp, off_t off, off_t len, kauth_cred_t cred,
pagestart + ((i + 1) << PAGE_SHIFT) <= eob) {
pgs[i]->flags &= ~PG_RDONLY;
}
pgs[i]->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pgs[i], UVM_PAGE_STATUS_DIRTY);
}
uvm_pagelock(pgs[i]);
uvm_pageactivate(pgs[i]);

View File

@ -1,4 +1,4 @@
# $NetBSD: files.uvm,v 1.32 2019/12/27 12:51:57 ad Exp $
# $NetBSD: files.uvm,v 1.33 2020/01/15 17:55:45 ad Exp $
#
# UVM options
@ -38,6 +38,7 @@ file uvm/uvm_mremap.c uvm
file uvm/uvm_object.c uvm
file uvm/uvm_page.c uvm
file uvm/uvm_page_array.c uvm
file uvm/uvm_page_status.c uvm
file uvm/uvm_pager.c uvm
file uvm/uvm_pdaemon.c uvm
file uvm/uvm_pdpolicy_clock.c !pdpolicy_clockpro

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_anon.c,v 1.70 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_anon.c,v 1.71 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_anon.c,v 1.70 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_anon.c,v 1.71 2020/01/15 17:55:45 ad Exp $");
#include "opt_uvmhist.h"
@ -346,7 +346,7 @@ uvm_anon_pagein(struct vm_amap *amap, struct vm_anon *anon)
uvm_swap_free(anon->an_swslot, 1);
}
anon->an_swslot = 0;
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
/*
* Deactivate the page (to put it on a page queue).

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_aobj.c,v 1.133 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_aobj.c,v 1.134 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1998 Chuck Silvers, Charles D. Cranor and
@ -38,7 +38,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v 1.133 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v 1.134 2020/01/15 17:55:45 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_uvmhist.h"
@ -849,7 +849,8 @@ uao_get(struct uvm_object *uobj, voff_t offset, struct vm_page **pps,
if (ptmp) {
/* new page */
ptmp->flags &= ~(PG_FAKE);
ptmp->flags |= PG_AOBJ;
uvm_pagemarkdirty(ptmp,
UVM_PAGE_STATUS_UNKNOWN);
goto gotpage;
}
}
@ -870,6 +871,8 @@ uao_get(struct uvm_object *uobj, voff_t offset, struct vm_page **pps,
* useful page: busy/lock it and plug it in our
* result array
*/
KASSERT(uvm_pagegetdirty(ptmp) !=
UVM_PAGE_STATUS_CLEAN);
/* caller must un-busy this page */
ptmp->flags |= PG_BUSY;
@ -951,8 +954,6 @@ gotpage:
continue;
}
ptmp->flags |= PG_AOBJ;
/*
* got new page ready for I/O. break pps while
* loop. pps[lcv] is still NULL.
@ -980,6 +981,8 @@ gotpage:
* loop).
*/
KASSERT(uvm_pagegetdirty(ptmp) !=
UVM_PAGE_STATUS_CLEAN);
/* we own it, caller must un-busy */
ptmp->flags |= PG_BUSY;
UVM_PAGE_OWN(ptmp, "uao_get2");
@ -1060,10 +1063,11 @@ gotpage:
#endif /* defined(VMSWAP) */
}
if ((access_type & VM_PROT_WRITE) == 0) {
ptmp->flags |= PG_CLEAN;
pmap_clear_modify(ptmp);
}
/*
* note that we will allow the page being writably-mapped
* (!PG_RDONLY) regardless of access_type.
*/
uvm_pagemarkdirty(ptmp, UVM_PAGE_STATUS_UNKNOWN);
/*
* we got the page! clear the fake flag (indicates valid
@ -1075,7 +1079,8 @@ gotpage:
* => unbusy the page
* => activate the page
*/
KASSERT(uvm_pagegetdirty(ptmp) != UVM_PAGE_STATUS_CLEAN);
KASSERT((ptmp->flags & PG_FAKE) != 0);
ptmp->flags &= ~PG_FAKE;
pps[lcv] = ptmp;
}
@ -1308,7 +1313,8 @@ uao_pagein_page(struct uvm_aobj *aobj, int pageidx)
if (pg->flags & PG_WANTED) {
wakeup(pg);
}
pg->flags &= ~(PG_WANTED|PG_BUSY|PG_CLEAN|PG_FAKE);
pg->flags &= ~(PG_WANTED|PG_BUSY|PG_FAKE);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
UVM_PAGE_OWN(pg, NULL);
return false;

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_bio.c,v 1.102 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_bio.c,v 1.103 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1998 Chuck Silvers.
@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_bio.c,v 1.102 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_bio.c,v 1.103 2020/01/15 17:55:45 ad Exp $");
#include "opt_uvmhist.h"
#include "opt_ubc.h"
@ -230,13 +230,11 @@ static inline int
ubc_fault_page(const struct uvm_faultinfo *ufi, const struct ubc_map *umap,
struct vm_page *pg, vm_prot_t prot, vm_prot_t access_type, vaddr_t va)
{
struct uvm_object *uobj;
vm_prot_t mask;
int error;
bool rdonly;
uobj = pg->uobject;
KASSERT(mutex_owned(uobj->vmobjlock));
KASSERT(mutex_owned(pg->uobject->vmobjlock));
if (pg->flags & PG_WANTED) {
wakeup(pg);
@ -270,6 +268,9 @@ ubc_fault_page(const struct uvm_faultinfo *ufi, const struct ubc_map *umap,
/*
* Note that a page whose backing store is partially allocated
* is marked as PG_RDONLY.
*
* it's a responsibility of ubc_alloc's caller to allocate backing
* blocks before writing to the window.
*/
KASSERT((pg->flags & PG_RDONLY) == 0 ||
@ -277,9 +278,7 @@ ubc_fault_page(const struct uvm_faultinfo *ufi, const struct ubc_map *umap,
pg->offset < umap->writeoff ||
pg->offset + PAGE_SIZE > umap->writeoff + umap->writelen);
rdonly = ((access_type & VM_PROT_WRITE) == 0 &&
(pg->flags & PG_RDONLY) != 0) ||
UVM_OBJ_NEEDS_WRITEFAULT(uobj);
rdonly = uvm_pagereadonly_p(pg);
mask = rdonly ? ~VM_PROT_WRITE : VM_PROT_ALL;
error = pmap_enter(ufi->orig_map->pmap, va, VM_PAGE_TO_PHYS(pg),
@ -665,7 +664,10 @@ ubc_release(void *va, int flags)
umapva + slot_offset + (i << PAGE_SHIFT), &pa);
KASSERT(rv);
pgs[i] = PHYS_TO_VM_PAGE(pa);
pgs[i]->flags &= ~(PG_FAKE|PG_CLEAN);
pgs[i]->flags &= ~PG_FAKE;
KASSERTMSG(uvm_pagegetdirty(pgs[i]) ==
UVM_PAGE_STATUS_DIRTY,
"page %p not dirty", pgs[i]);
KASSERT(pgs[i]->loan_count == 0);
uvm_pagelock(pgs[i]);
uvm_pageactivate(pgs[i]);
@ -896,9 +898,18 @@ ubc_direct_release(struct uvm_object *uobj,
uvm_pageactivate(pg);
uvm_pageunlock(pg);
/* Page was changed, no longer fake and neither clean */
if (flags & UBC_WRITE)
pg->flags &= ~(PG_FAKE|PG_CLEAN);
/*
* Page was changed, no longer fake and neither clean.
* There's no managed mapping in the direct case, so
* mark the page dirty manually.
*/
if (flags & UBC_WRITE) {
pg->flags &= ~PG_FAKE;
KASSERTMSG(uvm_pagegetdirty(pg) ==
UVM_PAGE_STATUS_DIRTY,
"page %p not dirty", pg);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
}
}
uvm_page_unbusy(pgs, npages);
mutex_exit(uobj->vmobjlock);

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_extern.h,v 1.218 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_extern.h,v 1.219 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -500,6 +500,12 @@ struct uvmexp_sysctl {
int64_t poolpages;
int64_t countsyncone;
int64_t countsyncall;
int64_t anonunknown;
int64_t anonclean;
int64_t anondirty;
int64_t fileunknown;
int64_t fileclean;
int64_t filedirty;
};
#ifdef _KERNEL
@ -779,10 +785,12 @@ int uvm_grow(struct proc *, vaddr_t);
void uvm_deallocate(struct vm_map *, vaddr_t, vsize_t);
/* uvm_vnode.c */
struct uvm_page_array;
void uvm_vnp_setsize(struct vnode *, voff_t);
void uvm_vnp_setwritesize(struct vnode *, voff_t);
int uvn_findpages(struct uvm_object *, voff_t,
int *, struct vm_page **, int);
unsigned int *, struct vm_page **,
struct uvm_page_array *, unsigned int);
bool uvn_text_p(struct uvm_object *);
bool uvn_clean_p(struct uvm_object *);
bool uvn_needs_writefault_p(struct uvm_object *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_fault.c,v 1.214 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_fault.c,v 1.215 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_fault.c,v 1.214 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_fault.c,v 1.215 2020/01/15 17:55:45 ad Exp $");
#include "opt_uvmhist.h"
@ -378,7 +378,7 @@ uvmfault_anonget(struct uvm_faultinfo *ufi, struct vm_amap *amap,
uvmfault_unlockall(ufi, amap, NULL);
/*
* Pass a PG_BUSY+PG_FAKE+PG_CLEAN page into
* Pass a PG_BUSY+PG_FAKE clean page into
* the uvm_swap_get() function with all data
* structures unlocked. Note that it is OK
* to read an_swslot here, because we hold
@ -488,6 +488,7 @@ released:
uvm_pageactivate(pg);
uvm_pageunlock(pg);
pg->flags &= ~(PG_WANTED|PG_BUSY|PG_FAKE);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_UNKNOWN);
UVM_PAGE_OWN(pg, NULL);
#else
panic("%s: we_own", __func__);
@ -640,6 +641,7 @@ uvmfault_promote(struct uvm_faultinfo *ufi,
if (opg) {
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);
@ -782,7 +784,7 @@ static inline void uvm_fault_lower_lookup(
struct vm_page **);
static inline void uvm_fault_lower_neighbor(
struct uvm_faultinfo *, const struct uvm_faultctx *,
vaddr_t, struct vm_page *, bool);
vaddr_t, struct vm_page *);
static inline int uvm_fault_lower_io(
struct uvm_faultinfo *, const struct uvm_faultctx *,
struct uvm_object **, struct vm_page **);
@ -1256,6 +1258,11 @@ uvm_fault_upper_neighbor(
/* locked: amap, anon */
KASSERT(pg->uobject == NULL);
KASSERT(pg->uanon != NULL);
KASSERT(mutex_owned(pg->uanon->an_lock));
KASSERT(uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN);
uvm_pagelock(pg);
uvm_pageenqueue(pg);
uvm_pageunlock(pg);
@ -1535,6 +1542,7 @@ uvm_fault_upper_enter(
KASSERT(anon->an_lock == amap->am_lock);
KASSERT(oanon->an_lock == amap->am_lock);
KASSERT(uobj == NULL || mutex_owned(uobj->vmobjlock));
KASSERT(uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN);
/*
* now map the page in.
@ -1612,21 +1620,20 @@ uvm_fault_upper_done(
uvm_pagelock(pg);
if (wire_paging) {
uvm_pagewire(pg);
/*
* since the now-wired page cannot be paged out,
* release its swap resources for others to use.
* since an anon with no swap cannot be PG_CLEAN,
* clear its clean flag now.
*/
pg->flags &= ~(PG_CLEAN);
} else {
uvm_pageactivate(pg);
}
uvm_pageunlock(pg);
if (wire_paging) {
/*
* since the now-wired page cannot be paged out,
* release its swap resources for others to use.
* and since an anon with no swap cannot be clean,
* mark it dirty now.
*/
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
uvm_anon_dropswap(anon);
}
}
@ -1744,7 +1751,7 @@ uvm_fault_lower(
KASSERT(uobjpage != NULL);
KASSERT(uobj == NULL || uobj == uobjpage->uobject);
KASSERT(uobj == NULL || !UVM_OBJ_IS_CLEAN(uobjpage->uobject) ||
(uobjpage->flags & PG_CLEAN) != 0);
uvm_pagegetdirty(uobjpage) == UVM_PAGE_STATUS_CLEAN);
if (!flt->promote) {
error = uvm_fault_lower_direct(ufi, flt, uobj, uobjpage);
@ -1813,12 +1820,7 @@ uvm_fault_lower_lookup(
UVMHIST_LOG(maphist, " got uobjpage (0x%#jx) "
"with locked get", (uintptr_t)curpg, 0, 0, 0);
} else {
bool readonly = (curpg->flags & PG_RDONLY)
|| (curpg->loan_count > 0)
|| UVM_OBJ_NEEDS_WRITEFAULT(curpg->uobject);
uvm_fault_lower_neighbor(ufi, flt,
currva, curpg, readonly);
uvm_fault_lower_neighbor(ufi, flt, currva, curpg);
}
}
pmap_update(ufi->orig_map->pmap);
@ -1831,8 +1833,9 @@ uvm_fault_lower_lookup(
static void
uvm_fault_lower_neighbor(
struct uvm_faultinfo *ufi, const struct uvm_faultctx *flt,
vaddr_t currva, struct vm_page *pg, bool readonly)
vaddr_t currva, struct vm_page *pg)
{
const bool readonly = uvm_pagereadonly_p(pg) || pg->loan_count > 0;
UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
/* locked: maps(read), amap(if there), uobj */
@ -1861,7 +1864,8 @@ uvm_fault_lower_neighbor(
KASSERT((pg->flags & PG_PAGEOUT) == 0);
KASSERT((pg->flags & PG_RELEASED) == 0);
KASSERT((pg->flags & PG_WANTED) == 0);
KASSERT(!UVM_OBJ_IS_CLEAN(pg->uobject) || (pg->flags & PG_CLEAN) != 0);
KASSERT(!UVM_OBJ_IS_CLEAN(pg->uobject) ||
uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_CLEAN);
pg->flags &= ~(PG_BUSY);
UVM_PAGE_OWN(pg, NULL);
@ -2216,6 +2220,7 @@ uvm_fault_lower_enter(
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("uvm_fault_lower_enter"); UVMHIST_CALLED(maphist);
@ -2241,12 +2246,16 @@ uvm_fault_lower_enter(
" MAPPING: case2: pm=%#jx, va=%#jx, pg=%#jx, promote=%jd",
(uintptr_t)ufi->orig_map->pmap, ufi->orig_rvaddr,
(uintptr_t)pg, flt->promote);
KASSERT((flt->access_type & VM_PROT_WRITE) == 0 ||
(pg->flags & PG_RDONLY) == 0);
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),
(pg->flags & PG_RDONLY) != 0 ?
flt->enter_prot & ~VM_PROT_WRITE : flt->enter_prot,
readonly ? flt->enter_prot & ~VM_PROT_WRITE : flt->enter_prot,
flt->access_type | PMAP_CANFAIL |
(flt->wire_mapping ? PMAP_WIRED : 0)) != 0) {
@ -2332,12 +2341,12 @@ uvm_fault_lower_done(
/*
* since the now-wired page cannot be paged out,
* release its swap resources for others to use.
* since an aobj page with no swap cannot be PG_CLEAN,
* clear its clean flag now.
* since an aobj page with no swap cannot be clean,
* mark it dirty now.
*/
KASSERT(uobj != NULL);
pg->flags &= ~(PG_CLEAN);
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
dropswap = true;
}
} else {

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_loan.c,v 1.93 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_loan.c,v 1.94 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v 1.93 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v 1.94 2020/01/15 17:55:45 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -1126,22 +1126,13 @@ uvm_loanbreak(struct vm_page *uobjpage)
* one and clear the fake flags on the new page (keep it busy).
* force a reload of the old page by clearing it from all
* pmaps.
* transfer dirtiness of the old page to the new page.
* then rename the pages.
*/
uvm_pagecopy(uobjpage, pg); /* old -> new */
pg->flags &= ~PG_FAKE;
KASSERT(uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_DIRTY);
pmap_page_protect(uobjpage, VM_PROT_NONE);
if ((uobjpage->flags & PG_CLEAN) != 0 && !pmap_clear_modify(uobjpage)) {
pmap_clear_modify(pg);
pg->flags |= PG_CLEAN;
} else {
/* uvm_pagecopy marked it dirty */
KASSERT((pg->flags & PG_CLEAN) == 0);
/* a object with a dirty page should be dirty. */
KASSERT(!UVM_OBJ_IS_CLEAN(uobj));
}
if (uobjpage->flags & PG_WANTED)
wakeup(uobjpage);
/* uobj still locked */
@ -1184,9 +1175,11 @@ int
uvm_loanbreak_anon(struct vm_anon *anon, struct uvm_object *uobj)
{
struct vm_page *newpg, *oldpg;
unsigned oldstatus;
KASSERT(mutex_owned(anon->an_lock));
KASSERT(uobj == NULL || mutex_owned(uobj->vmobjlock));
KASSERT(anon->an_page->loan_count > 0);
/* get new un-owned replacement page */
newpg = uvm_pagealloc(NULL, 0, NULL, 0);
@ -1197,24 +1190,29 @@ uvm_loanbreak_anon(struct vm_anon *anon, struct uvm_object *uobj)
oldpg = anon->an_page;
/* copy old -> new */
uvm_pagecopy(oldpg, newpg);
KASSERT(uvm_pagegetdirty(newpg) == UVM_PAGE_STATUS_DIRTY);
/* force reload */
pmap_page_protect(oldpg, VM_PROT_NONE);
oldstatus = uvm_pagegetdirty(anon->an_page);
uvm_pagelock2(oldpg, newpg);
if (uobj == NULL) {
/*
* we were the lender (A->K); need to remove the page from
* pageq's.
*
* PG_ANON is updated by the caller.
*/
KASSERT((oldpg->flags & PG_ANON) != 0);
oldpg->flags &= ~PG_ANON;
uvm_pagedequeue(oldpg);
}
oldpg->uanon = NULL;
/* in case we owned */
oldpg->flags &= ~PG_ANON;
if (uobj) {
/* if we were receiver of loan */
KASSERT((oldpg->pqflags & PG_ANON) == 0);
oldpg->loan_count--;
}
@ -1234,6 +1232,13 @@ uvm_loanbreak_anon(struct vm_anon *anon, struct uvm_object *uobj)
}
/* done! */
kpreempt_disable();
if (uobj != NULL) {
CPU_COUNT(CPU_COUNT_ANONPAGES, 1);
} else {
CPU_COUNT(CPU_COUNT_ANONUNKNOWN + oldstatus, -1);
}
CPU_COUNT(CPU_COUNT_ANONDIRTY, 1);
kpreempt_enable();
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_meter.c,v 1.73 2019/12/31 13:07:14 ad Exp $ */
/* $NetBSD: uvm_meter.c,v 1.74 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -36,7 +36,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_meter.c,v 1.73 2019/12/31 13:07:14 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_meter.c,v 1.74 2020/01/15 17:55:45 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -180,6 +180,12 @@ sysctl_vm_uvmexp2(SYSCTLFN_ARGS)
u.poolpages = pool_totalpages();
u.countsyncone = cpu_count_get(CPU_COUNT_SYNC_ONE);
u.countsyncall = cpu_count_get(CPU_COUNT_SYNC_ALL);
u.anonunknown = (int)cpu_count_get(CPU_COUNT_ANONUNKNOWN);
u.anonclean = (int)cpu_count_get(CPU_COUNT_ANONCLEAN);
u.anondirty = (int)cpu_count_get(CPU_COUNT_ANONDIRTY);
u.fileunknown = (int)cpu_count_get(CPU_COUNT_FILEUNKNOWN);
u.fileclean = (int)cpu_count_get(CPU_COUNT_FILECLEAN);
u.filedirty = (int)cpu_count_get(CPU_COUNT_FILEDIRTY);
node = *rnode;
node.sysctl_data = &u;

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_object.c,v 1.19 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_object.c,v 1.20 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 2006, 2010, 2019 The NetBSD Foundation, Inc.
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_object.c,v 1.19 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_object.c,v 1.20 2020/01/15 17:55:45 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@ -174,7 +174,8 @@ uvm_obj_wirepages(struct uvm_object *uobj, off_t start, off_t end,
}
if (pgs[i]->flags & PG_AOBJ) {
pgs[i]->flags &= ~(PG_CLEAN);
uvm_pagemarkdirty(pgs[i],
UVM_PAGE_STATUS_DIRTY);
uao_dropswap(uobj, i);
}
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_object.h,v 1.35 2019/12/15 21:11:35 ad Exp $ */
/* $NetBSD: uvm_object.h,v 1.36 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -62,6 +62,13 @@ struct uvm_object {
LIST_HEAD(,ubc_map) uo_ubc; /* ubc mappings */
};
/*
* tags for uo_pages
*/
#define UVM_PAGE_DIRTY_TAG 1 /* might be dirty (!PG_CLEAN) */
#define UVM_PAGE_WRITEBACK_TAG 2 /* being written back */
/*
* UVM_OBJ_KERN is a 'special' uo_refs value which indicates that the
* object is a kernel memory object rather than a normal one (kernel

View File

@ -1,7 +1,7 @@
/* $NetBSD: uvm_page.c,v 1.223 2020/01/11 19:51:01 ad Exp $ */
/* $NetBSD: uvm_page.c,v 1.224 2020/01/15 17:55:45 ad Exp $ */
/*-
* Copyright (c) 2019 The NetBSD Foundation, Inc.
* Copyright (c) 2019, 2020 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -95,7 +95,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.223 2020/01/11 19:51:01 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.224 2020/01/15 17:55:45 ad Exp $");
#include "opt_ddb.h"
#include "opt_uvm.h"
@ -217,19 +217,31 @@ uvm_pageinsert_object(struct uvm_object *uobj, struct vm_page *pg)
KASSERT(mutex_owned(uobj->vmobjlock));
KASSERT((pg->flags & PG_TABLED) == 0);
if (UVM_OBJ_IS_VNODE(uobj)) {
if (uobj->uo_npages == 0) {
struct vnode *vp = (struct vnode *)uobj;
if ((pg->flags & PG_STAT) != 0) {
/* Cannot use uvm_pagegetdirty(): not yet in radix tree. */
const unsigned int status = pg->flags & (PG_CLEAN | PG_DIRTY);
const bool isaobj = (pg->flags & PG_AOBJ) != 0;
vholdl(vp);
}
if (UVM_OBJ_IS_VTEXT(uobj)) {
cpu_count(CPU_COUNT_EXECPAGES, 1);
if (!isaobj) {
KASSERT((pg->flags & PG_FILE) != 0);
if (uobj->uo_npages == 0) {
struct vnode *vp = (struct vnode *)uobj;
vholdl(vp);
}
kpreempt_disable();
if (UVM_OBJ_IS_VTEXT(uobj)) {
CPU_COUNT(CPU_COUNT_EXECPAGES, 1);
} else {
CPU_COUNT(CPU_COUNT_FILEPAGES, 1);
}
CPU_COUNT(CPU_COUNT_FILEUNKNOWN + status, 1);
} else {
cpu_count(CPU_COUNT_FILEPAGES, 1);
kpreempt_disable();
CPU_COUNT(CPU_COUNT_ANONPAGES, 1);
CPU_COUNT(CPU_COUNT_ANONUNKNOWN + status, 1);
}
} else if (UVM_OBJ_IS_AOBJ(uobj)) {
cpu_count(CPU_COUNT_ANONPAGES, 1);
kpreempt_enable();
}
pg->flags |= PG_TABLED;
uobj->uo_npages++;
@ -245,6 +257,11 @@ uvm_pageinsert_tree(struct uvm_object *uobj, struct vm_page *pg)
if (error != 0) {
return error;
}
if ((pg->flags & PG_CLEAN) == 0) {
radix_tree_set_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG);
}
KASSERT(((pg->flags & PG_CLEAN) == 0) ==
radix_tree_get_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG));
return 0;
}
@ -262,22 +279,32 @@ uvm_pageremove_object(struct uvm_object *uobj, struct vm_page *pg)
KASSERT(mutex_owned(uobj->vmobjlock));
KASSERT(pg->flags & PG_TABLED);
if (UVM_OBJ_IS_VNODE(uobj)) {
if (uobj->uo_npages == 1) {
struct vnode *vp = (struct vnode *)uobj;
if ((pg->flags & PG_STAT) != 0) {
/* Cannot use uvm_pagegetdirty(): no longer in radix tree. */
const unsigned int status = pg->flags & (PG_CLEAN | PG_DIRTY);
const bool isaobj = (pg->flags & PG_AOBJ) != 0;
holdrelel(vp);
}
if (UVM_OBJ_IS_VTEXT(uobj)) {
cpu_count(CPU_COUNT_EXECPAGES, -1);
if (!isaobj) {
KASSERT((pg->flags & PG_FILE) != 0);
if (uobj->uo_npages == 1) {
struct vnode *vp = (struct vnode *)uobj;
holdrelel(vp);
}
kpreempt_disable();
if (UVM_OBJ_IS_VTEXT(uobj)) {
CPU_COUNT(CPU_COUNT_EXECPAGES, -1);
} else {
CPU_COUNT(CPU_COUNT_FILEPAGES, -1);
}
CPU_COUNT(CPU_COUNT_FILEUNKNOWN + status, -1);
} else {
cpu_count(CPU_COUNT_FILEPAGES, -1);
kpreempt_disable();
CPU_COUNT(CPU_COUNT_ANONPAGES, -1);
CPU_COUNT(CPU_COUNT_ANONUNKNOWN + status, -1);
}
} else if (UVM_OBJ_IS_AOBJ(uobj)) {
cpu_count(CPU_COUNT_ANONPAGES, -1);
kpreempt_enable();
}
/* object should be locked */
uobj->uo_npages--;
pg->flags &= ~PG_TABLED;
pg->uobject = NULL;
@ -1290,6 +1317,7 @@ uvm_pagealloc_strat(struct uvm_object *obj, voff_t off, struct vm_anon *anon,
}
if (anon) {
CPU_COUNT(CPU_COUNT_ANONPAGES, 1);
CPU_COUNT(CPU_COUNT_ANONCLEAN, 1);
}
splx(s);
KASSERT((pg->flags & ~(PG_ZERO|PG_FREE)) == 0);
@ -1312,6 +1340,14 @@ uvm_pagealloc_strat(struct uvm_object *obj, voff_t off, struct vm_anon *anon,
pg->flags |= PG_ANON;
mutex_exit(&pg->interlock);
} else if (obj) {
/*
* set PG_FILE|PG_AOBJ before the first uvm_pageinsert.
*/
if (UVM_OBJ_IS_VNODE(obj)) {
pg->flags |= PG_FILE;
} else {
pg->flags |= PG_AOBJ;
}
uvm_pageinsert_object(obj, pg);
mutex_exit(&pg->interlock);
error = uvm_pageinsert_tree(obj, pg);
@ -1334,9 +1370,12 @@ uvm_pagealloc_strat(struct uvm_object *obj, voff_t off, struct vm_anon *anon,
* A zero'd page is not clean. If we got a page not already
* zero'd, then we have to zero it ourselves.
*/
pg->flags &= ~PG_CLEAN;
if (zeroit)
if (obj != NULL || anon != NULL) {
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
}
if (zeroit) {
pmap_zero_page(VM_PAGE_TO_PHYS(pg));
}
}
return(pg);
@ -1354,6 +1393,7 @@ uvm_pagereplace(struct vm_page *oldpg, struct vm_page *newpg)
{
struct uvm_object *uobj = oldpg->uobject;
struct vm_page *pg __diagused;
uint64_t idx;
KASSERT((oldpg->flags & PG_TABLED) != 0);
KASSERT(uobj != NULL);
@ -1363,12 +1403,25 @@ uvm_pagereplace(struct vm_page *oldpg, struct vm_page *newpg)
KASSERT(mutex_owned(&oldpg->interlock));
KASSERT(mutex_owned(&newpg->interlock));
newpg->offset = oldpg->offset;
pg = radix_tree_replace_node(&uobj->uo_pages,
newpg->offset >> PAGE_SHIFT, newpg);
KASSERT(pg == oldpg);
newpg->uobject = uobj;
newpg->offset = oldpg->offset;
idx = newpg->offset >> PAGE_SHIFT;
pg = radix_tree_replace_node(&uobj->uo_pages, idx, newpg);
KASSERT(pg == oldpg);
if (((oldpg->flags ^ newpg->flags) & PG_CLEAN) != 0) {
if ((newpg->flags & PG_CLEAN) != 0) {
radix_tree_clear_tag(&uobj->uo_pages, idx,
UVM_PAGE_DIRTY_TAG);
} else {
radix_tree_set_tag(&uobj->uo_pages, idx,
UVM_PAGE_DIRTY_TAG);
}
}
/*
* oldpg's PG_STAT is stable. newpg is not reachable by others yet.
*/
newpg->flags |=
(newpg->flags & ~PG_STAT) | (oldpg->flags & PG_STAT);
uvm_pageinsert_object(uobj, newpg);
uvm_pageremove_object(uobj, oldpg);
}
@ -1502,7 +1555,7 @@ uvm_pagefree(struct vm_page *pg)
locked = true;
if (pg->uobject != NULL) {
uvm_pageremove_object(pg->uobject, pg);
pg->flags &= ~PG_CLEAN;
pg->flags &= ~(PG_FILE|PG_AOBJ);
} else if (pg->uanon != NULL) {
if ((pg->flags & PG_ANON) == 0) {
pg->loan_count--;
@ -1520,6 +1573,7 @@ uvm_pagefree(struct vm_page *pg)
#ifdef UVM_PAGE_TRKOWN
pg->owner_tag = NULL;
#endif
KASSERT((pg->flags & PG_STAT) == 0);
if (pg->loan_count) {
KASSERT(pg->uobject == NULL);
if (pg->uanon == NULL) {
@ -1542,9 +1596,13 @@ uvm_pagefree(struct vm_page *pg)
if (pg->uobject != NULL) {
uvm_pageremove_object(pg->uobject, pg);
} else if (pg->uanon != NULL) {
const unsigned int status = uvm_pagegetdirty(pg);
pg->uanon->an_page = NULL;
pg->uanon = NULL;
cpu_count(CPU_COUNT_ANONPAGES, -1);
kpreempt_disable();
CPU_COUNT(CPU_COUNT_ANONPAGES, -1);
CPU_COUNT(CPU_COUNT_ANONUNKNOWN + status, -1);
kpreempt_enable();
}
/*
@ -1953,7 +2011,8 @@ uvm_pageunlock2(struct vm_page *pg1, struct vm_page *pg2)
void
uvm_pagezero(struct vm_page *pg)
{
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
pmap_zero_page(VM_PAGE_TO_PHYS(pg));
}
@ -1968,7 +2027,7 @@ void
uvm_pagecopy(struct vm_page *src, struct vm_page *dst)
{
dst->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(dst, UVM_PAGE_STATUS_DIRTY);
pmap_copy_page(VM_PAGE_TO_PHYS(src), VM_PAGE_TO_PHYS(dst));
}
@ -2015,6 +2074,29 @@ uvm_page_owner_locked_p(struct vm_page *pg)
return true;
}
/*
* uvm_pagereadonly_p: return if the page should be mapped read-only
*/
bool
uvm_pagereadonly_p(struct vm_page *pg)
{
struct uvm_object * const uobj = pg->uobject;
KASSERT(uobj == NULL || mutex_owned(uobj->vmobjlock));
KASSERT(uobj != NULL || mutex_owned(pg->uanon->an_lock));
if ((pg->flags & PG_RDONLY) != 0) {
return true;
}
if (uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_CLEAN) {
return true;
}
if (uobj == NULL) {
return false;
}
return UVM_OBJ_NEEDS_WRITEFAULT(uobj);
}
#ifdef PMAP_DIRECT
/*
* Call pmap to translate physical address into a virtual and to run a callback
@ -2080,7 +2162,7 @@ uvm_page_printit(struct vm_page *pg, bool full,
(*pr)("PAGE %p:\n", pg);
snprintb(pgbuf, sizeof(pgbuf), page_flagbits, pg->flags);
(*pr)(" flags=%s, pqflags=%x, wire_count=%d, pa=0x%lx\n",
(*pr)(" flags=%s\n pqflags=%x, wire_count=%d, pa=0x%lx\n",
pgbuf, pg->pqflags, pg->wire_count, (long)VM_PAGE_TO_PHYS(pg));
(*pr)(" uobject=%p, uanon=%p, offset=0x%llx loan_count=%d\n",
pg->uobject, pg->uanon, (long long)pg->offset, pg->loan_count);

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_page.h,v 1.95 2020/01/10 21:32:17 ad Exp $ */
/* $NetBSD: uvm_page.h,v 1.96 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -160,8 +160,7 @@ struct vm_page {
TAILQ_ENTRY(vm_page) pdqueue; /* p: pagedaemon queue */
kmutex_t interlock; /* s: lock on identity */
uint32_t pqflags; /* i: pagedaemon flags */
uint16_t flags; /* o: object flags */
uint16_t spare; /* : spare for now */
uint32_t flags; /* o: object flags */
paddr_t phys_addr; /* o: physical address of pg */
uint32_t loan_count; /* o,i: num. active loans */
uint32_t wire_count; /* o,i: wired down map refs */
@ -194,6 +193,15 @@ struct vm_page {
*
* Flag descriptions:
*
* PG_CLEAN:
* Page is known clean.
* The contents of the page is consistent with its backing store.
*
* PG_DIRTY:
* Page is known dirty.
* To avoid losing data, the contents of the page should be written
* back to the backing store before freeing the page.
*
* PG_BUSY:
* Page is long-term locked, usually because of I/O (transfer from the
* page memory to the backing store) is in progress. LWP attempting
@ -205,31 +213,20 @@ struct vm_page {
* responsible to clear both flags and wake up any waiters once it has
* released the long-term lock (PG_BUSY).
*
* PG_PAGEOUT:
* Indicates that the page is being paged-out in preparation for
* being freed.
*
* PG_RELEASED:
* Indicates that the page, which is currently PG_BUSY, should be freed
* after the release of long-term lock. It is responsibility of the
* owning LWP (i.e. which set PG_BUSY) to do it.
*
* PG_CLEAN:
* Page has not been modified since it was loaded from the backing
* store. If this flag is not set, page is considered "dirty".
* XXX: Currently it means that the page *might* be clean; will be
* fixed with yamt-pagecache merge.
*
* PG_FAKE:
* Page has been allocated, but not yet initialised. The flag is used
* to avoid overwriting of valid data, e.g. to prevent read from the
* backing store when in-core data is newer.
*
* PG_TABLED:
* Indicates that the page is currently in the object's offset queue,
* and that it should be removed from it once the page is freed. Used
* diagnostic purposes.
*
* PG_PAGEOUT:
* Indicates that the page is being paged-out in preparation for
* being freed.
*
* PG_RDONLY:
* Indicates that the page must be mapped read-only.
*
@ -239,31 +236,43 @@ struct vm_page {
* page is placed on the free list.
*
* PG_MARKER:
* Dummy marker page.
* Dummy marker page, generally used for list traversal.
*/
#define PG_BUSY 0x0001
#define PG_WANTED 0x0002
#define PG_TABLED 0x0004
#define PG_CLEAN 0x0008
#define PG_PAGEOUT 0x0010
#define PG_RELEASED 0x0020
#define PG_FAKE 0x0040
#define PG_RDONLY 0x0080
#define PG_AOBJ 0x0100 /* page is part of an anonymous
/*
* if you want to renumber PG_CLEAN and PG_DIRTY, check __CTASSERTs in
* uvm_page_status.c first.
*/
#define PG_CLEAN 0x00000001 /* page is known clean */
#define PG_DIRTY 0x00000002 /* page is known dirty */
#define PG_BUSY 0x00000004 /* page is locked */
#define PG_WANTED 0x00000008 /* someone is waiting for page */
#define PG_PAGEOUT 0x00000010 /* page to be freed for pagedaemon */
#define PG_RELEASED 0x00000020 /* page to be freed when unbusied */
#define PG_FAKE 0x00000040 /* page is not yet initialized */
#define PG_RDONLY 0x00000080 /* page must be mapped read-only */
#define PG_ZERO 0x00000100 /* page is pre-zero'd */
#define PG_TABLED 0x00000200 /* page is tabled in object */
#define PG_AOBJ 0x00000400 /* page is part of an anonymous
uvm_object */
#define PG_ANON 0x0200 /* page is part of an anon, rather
#define PG_ANON 0x00000800 /* page is part of an anon, rather
than an uvm_object */
#define PG_SWAPBACKED (PG_ANON|PG_AOBJ)
#define PG_READAHEAD 0x0400 /* read-ahead but not "hit" yet */
#define PG_FREE 0x0800 /* page is on free list */
#define PG_MARKER 0x1000
#define PG_PAGER1 0x2000 /* pager-specific flag */
#define PG_ZERO 0x4000
#define PG_FILE 0x00001000 /* file backed (non-anonymous) */
#define PG_READAHEAD 0x00002000 /* read-ahead but not "hit" yet */
#define PG_FREE 0x00004000 /* page is on free list */
#define PG_MARKER 0x00008000 /* dummy marker page */
#define PG_PAGER1 0x00010000 /* pager-specific flag */
#define PG_STAT (PG_ANON|PG_AOBJ|PG_FILE)
#define PG_SWAPBACKED (PG_ANON|PG_AOBJ)
#define UVM_PGFLAGBITS \
"\20\1BUSY\2WANTED\3TABLED\4CLEAN\5PAGEOUT\6RELEASED\7FAKE\10RDONLY" \
"\11AOBJ\12AOBJ\13READAHEAD\14FREE\15MARKER\16PAGER1\17ZERO"
"\20\1CLEAN\2DIRTY\3BUSY\4WANTED" \
"\5PAGEOUT\6RELEASED\7FAKE\10RDONLY" \
"\11ZERO\12TABLED\13AOBJ\14ANON" \
"\15FILE\16READAHEAD\17FREE\20MARKER" \
"\21PAGER1"
/*
* uvmpdpol state flags.
@ -343,6 +352,11 @@ bool uvm_pageismanaged(paddr_t);
bool uvm_page_owner_locked_p(struct vm_page *);
void uvm_pgfl_lock(void);
void uvm_pgfl_unlock(void);
unsigned int uvm_pagegetdirty(struct vm_page *);
void uvm_pagemarkdirty(struct vm_page *, unsigned int);
bool uvm_pagecheckdirty(struct vm_page *, bool);
bool uvm_pagereadonly_p(struct vm_page *);
bool uvm_page_locked_p(struct vm_page *);
int uvm_page_lookup_freelist(struct vm_page *);
@ -355,6 +369,23 @@ int uvm_direct_process(struct vm_page **, u_int, voff_t, vsize_t,
int (*)(void *, size_t, void *), void *);
#endif
/*
* page dirtiness status for uvm_pagegetdirty and uvm_pagemarkdirty
*
* UNKNOWN means that we need to consult pmap to know if the page is
* dirty or not.
* basically, UVM_PAGE_STATUS_CLEAN implies that the page has no writable
* mapping.
*
* if you want to renumber these, check __CTASSERTs in
* uvm_page_status.c first.
*/
#define UVM_PAGE_STATUS_UNKNOWN 0
#define UVM_PAGE_STATUS_CLEAN 1
#define UVM_PAGE_STATUS_DIRTY 2
#define UVM_PAGE_NUM_STATUS 3
/*
* macros
*/

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_page_array.c,v 1.2 2019/12/15 21:11:35 ad Exp $ */
/* $NetBSD: uvm_page_array.c,v 1.3 2020/01/15 17:55:45 ad Exp $ */
/*-
* Copyright (c)2011 YAMAMOTO Takashi,
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_page_array.c,v 1.2 2019/12/15 21:11:35 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_page_array.c,v 1.3 2020/01/15 17:55:45 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -142,7 +142,6 @@ uvm_page_array_fill(struct uvm_page_array *ar, struct uvm_object *uobj,
KASSERT(mutex_owned(uobj->vmobjlock));
#endif
KASSERT(uvm_page_array_peek(ar) == NULL);
#if 0 /* not merged from yamt-pagecache yet */
if ((flags & UVM_PAGE_ARRAY_FILL_DIRTY) != 0) {
unsigned int tagmask = UVM_PAGE_DIRTY_TAG;
@ -154,9 +153,7 @@ uvm_page_array_fill(struct uvm_page_array *ar, struct uvm_object *uobj,
radix_tree_gang_lookup_tagged_node)(
&uobj->uo_pages, off >> PAGE_SHIFT, (void **)ar->ar_pages,
maxpages, dense, tagmask);
} else
#endif
{
} else {
npages =
(backward ? radix_tree_gang_lookup_node_reverse :
radix_tree_gang_lookup_node)(

194
sys/uvm/uvm_page_status.c Normal file
View File

@ -0,0 +1,194 @@
/* $NetBSD: uvm_page_status.c,v 1.2 2020/01/15 17:55:45 ad Exp $ */
/*-
* Copyright (c)2011 YAMAMOTO Takashi,
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_page_status.c,v 1.2 2020/01/15 17:55:45 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <uvm/uvm.h>
/*
* page dirtiness status tracking
*
* separated from uvm_page.c mainly for rump
*/
/*
* these constants are chosen to match so that we can convert between
* them quickly.
*/
__CTASSERT(UVM_PAGE_STATUS_UNKNOWN == 0);
__CTASSERT(UVM_PAGE_STATUS_DIRTY == PG_DIRTY);
__CTASSERT(UVM_PAGE_STATUS_CLEAN == PG_CLEAN);
/*
* uvm_pagegetdirty: return the dirtiness status (one of UVM_PAGE_STATUS_
* values) of the page.
*
* called with the owner locked.
*/
unsigned int
uvm_pagegetdirty(struct vm_page *pg)
{
struct uvm_object * const uobj __diagused = pg->uobject;
const uint64_t idx __diagused = pg->offset >> PAGE_SHIFT;
KASSERT((~pg->flags & (PG_CLEAN|PG_DIRTY)) != 0);
KASSERT(uvm_page_owner_locked_p(pg));
KASSERT(uobj == NULL || ((pg->flags & PG_CLEAN) == 0) ==
!!radix_tree_get_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG));
return pg->flags & (PG_CLEAN|PG_DIRTY);
}
/*
* uvm_pagemarkdirty: set the dirtiness status (one of UVM_PAGE_STATUS_ values)
* of the page.
*
* called with the owner locked.
*
* update the radix tree tag for object-owned page.
*
* if new status is UVM_PAGE_STATUS_UNKNOWN, clear pmap-level dirty bit
* so that later uvm_pagecheckdirty() can notice modifications on the page.
*/
void
uvm_pagemarkdirty(struct vm_page *pg, unsigned int newstatus)
{
struct uvm_object * const uobj = pg->uobject;
const uint64_t idx = pg->offset >> PAGE_SHIFT;
const unsigned int oldstatus = uvm_pagegetdirty(pg);
enum cpu_count base;
KASSERT((~newstatus & (PG_CLEAN|PG_DIRTY)) != 0);
KASSERT((newstatus & ~(PG_CLEAN|PG_DIRTY)) == 0);
KASSERT(uvm_page_owner_locked_p(pg));
KASSERT(uobj == NULL || ((pg->flags & PG_CLEAN) == 0) ==
!!radix_tree_get_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG));
if (oldstatus == newstatus) {
return;
}
/*
* set UVM_PAGE_DIRTY_TAG tag unless known CLEAN so that putpages can
* find possibly-dirty pages quickly.
*/
if (uobj != NULL) {
if (newstatus == UVM_PAGE_STATUS_CLEAN) {
radix_tree_clear_tag(&uobj->uo_pages, idx,
UVM_PAGE_DIRTY_TAG);
} else {
radix_tree_set_tag(&uobj->uo_pages, idx,
UVM_PAGE_DIRTY_TAG);
}
}
if (newstatus == UVM_PAGE_STATUS_UNKNOWN) {
/*
* start relying on pmap-level dirtiness tracking.
*/
pmap_clear_modify(pg);
}
pg->flags &= ~(PG_CLEAN|PG_DIRTY);
pg->flags |= newstatus;
KASSERT(uobj == NULL || ((pg->flags & PG_CLEAN) == 0) ==
!!radix_tree_get_tag(&uobj->uo_pages, idx, UVM_PAGE_DIRTY_TAG));
if ((pg->flags & PG_STAT) != 0) {
if ((pg->flags & PG_SWAPBACKED) != 0) {
base = CPU_COUNT_ANONUNKNOWN;
} else {
base = CPU_COUNT_FILEUNKNOWN;
}
kpreempt_disable();
CPU_COUNT(base + oldstatus, -1);
CPU_COUNT(base + newstatus, +1);
kpreempt_enable();
}
}
/*
* uvm_pagecheckdirty: check if page is dirty, and remove its dirty bit.
*
* called with the owner locked.
*
* returns if the page was dirty.
*
* if protected is true, mark the page CLEAN. otherwise, mark the page UNKNOWN.
* ("mark" in the sense of uvm_pagemarkdirty().)
*/
bool
uvm_pagecheckdirty(struct vm_page *pg, bool pgprotected)
{
const unsigned int oldstatus = uvm_pagegetdirty(pg);
bool modified;
KASSERT(uvm_page_owner_locked_p(pg));
/*
* if pgprotected is true, mark the page CLEAN.
* otherwise mark the page UNKNOWN unless it's CLEAN.
*
* possible transitions:
*
* CLEAN -> CLEAN , modified = false
* UNKNOWN -> UNKNOWN, modified = true
* UNKNOWN -> UNKNOWN, modified = false
* UNKNOWN -> CLEAN , modified = true
* UNKNOWN -> CLEAN , modified = false
* DIRTY -> UNKNOWN, modified = true
* DIRTY -> CLEAN , modified = true
*
* pmap_clear_modify is necessary if either of
* oldstatus or newstatus is UVM_PAGE_STATUS_UNKNOWN.
*/
if (oldstatus == UVM_PAGE_STATUS_CLEAN) {
modified = false;
} else {
const unsigned int newstatus = pgprotected ?
UVM_PAGE_STATUS_CLEAN : UVM_PAGE_STATUS_UNKNOWN;
if (oldstatus == UVM_PAGE_STATUS_DIRTY) {
modified = true;
if (newstatus == UVM_PAGE_STATUS_UNKNOWN) {
pmap_clear_modify(pg);
}
} else {
KASSERT(oldstatus == UVM_PAGE_STATUS_UNKNOWN);
modified = pmap_clear_modify(pg);
}
uvm_pagemarkdirty(pg, newstatus);
}
return modified;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_pager.c,v 1.119 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_pager.c,v 1.120 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -32,7 +32,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_pager.c,v 1.119 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_pager.c,v 1.120 2020/01/15 17:55:45 ad Exp $");
#include "opt_uvmhist.h"
#include "opt_readahead.h"
@ -367,6 +367,13 @@ uvm_aio_aiodone_pages(struct vm_page **pgs, int npages, bool write, int error)
}
#endif /* defined(VMSWAP) */
if (write && uobj != NULL) {
KASSERT(radix_tree_get_tag(&uobj->uo_pages,
pg->offset >> PAGE_SHIFT, UVM_PAGE_WRITEBACK_TAG));
radix_tree_clear_tag(&uobj->uo_pages,
pg->offset >> PAGE_SHIFT, UVM_PAGE_WRITEBACK_TAG);
}
/*
* process errors. for reads, just mark the page to be freed.
* for writes, if the error was ENOMEM, we assume this was
@ -386,7 +393,7 @@ uvm_aio_aiodone_pages(struct vm_page **pgs, int npages, bool write, int error)
pg->flags &= ~PG_PAGEOUT;
pageout_done++;
}
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
uvm_pagelock(pg);
uvm_pageactivate(pg);
uvm_pageunlock(pg);
@ -413,8 +420,6 @@ uvm_aio_aiodone_pages(struct vm_page **pgs, int npages, bool write, int error)
/*
* if the page is PG_FAKE, this must have been a read to
* initialize the page. clear PG_FAKE and activate the page.
* we must also clear the pmap "modified" flag since it may
* still be set from the page's previous identity.
*/
if (pg->flags & PG_FAKE) {
@ -424,11 +429,10 @@ uvm_aio_aiodone_pages(struct vm_page **pgs, int npages, bool write, int error)
pg->flags |= PG_READAHEAD;
uvm_ra_total.ev_count++;
#endif /* defined(READAHEAD_STATS) */
KASSERT((pg->flags & PG_CLEAN) != 0);
KASSERT(uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_CLEAN);
uvm_pagelock(pg);
uvm_pageenqueue(pg);
uvm_pageunlock(pg);
pmap_clear_modify(pg);
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_pdaemon.c,v 1.122 2019/12/31 22:42:51 ad Exp $ */
/* $NetBSD: uvm_pdaemon.c,v 1.123 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon.c,v 1.122 2019/12/31 22:42:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon.c,v 1.123 2020/01/15 17:55:45 ad Exp $");
#include "opt_uvmhist.h"
#include "opt_readahead.h"
@ -609,14 +609,14 @@ uvmpd_dropswap(struct vm_page *pg)
if ((pg->flags & PG_ANON) && anon->an_swslot) {
uvm_swap_free(anon->an_swslot, 1);
anon->an_swslot = 0;
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
result = true;
} else if (pg->flags & PG_AOBJ) {
int slot = uao_set_swslot(pg->uobject,
pg->offset >> PAGE_SHIFT, 0);
if (slot) {
uvm_swap_free(slot, 1);
pg->flags &= ~PG_CLEAN;
uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
result = true;
}
}
@ -757,10 +757,14 @@ uvmpd_scan_queue(void)
*/
pmap_page_protect(p, VM_PROT_NONE);
if ((p->flags & PG_CLEAN) && pmap_clear_modify(p)) {
p->flags &= ~(PG_CLEAN);
if (uvm_pagegetdirty(p) == UVM_PAGE_STATUS_UNKNOWN) {
if (pmap_clear_modify(p)) {
uvm_pagemarkdirty(p, UVM_PAGE_STATUS_DIRTY);
} else {
uvm_pagemarkdirty(p, UVM_PAGE_STATUS_CLEAN);
}
}
if (p->flags & PG_CLEAN) {
if (uvm_pagegetdirty(p) != UVM_PAGE_STATUS_DIRTY) {
int slot;
int pageidx;

View File

@ -1,4 +1,4 @@
/* $NetBSD: uvm_vnode.c,v 1.104 2019/12/21 14:41:44 ad Exp $ */
/* $NetBSD: uvm_vnode.c,v 1.105 2020/01/15 17:55:45 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@ -45,7 +45,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: uvm_vnode.c,v 1.104 2019/12/21 14:41:44 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: uvm_vnode.c,v 1.105 2020/01/15 17:55:45 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_uvmhist.h"
@ -66,6 +66,7 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_vnode.c,v 1.104 2019/12/21 14:41:44 ad Exp $");
#include <uvm/uvm.h>
#include <uvm/uvm_readahead.h>
#include <uvm/uvm_page_array.h>
#ifdef UVMHIST
UVMHIST_DEFINE(ubchist);
@ -82,7 +83,8 @@ static int uvn_put(struct uvm_object *, voff_t, voff_t, int);
static void uvn_reference(struct uvm_object *);
static int uvn_findpage(struct uvm_object *, voff_t, struct vm_page **,
int);
unsigned int, struct uvm_page_array *a,
unsigned int);
/*
* master pager structure
@ -136,7 +138,6 @@ uvn_detach(struct uvm_object *uobj)
*
* => object must be locked on entry! VOP_PUTPAGES must unlock it.
* => flags: PGO_SYNCIO -- use sync. I/O
* => note: caller must set PG_CLEAN and pmap_clear_modify (if needed)
*/
static int
@ -201,16 +202,23 @@ uvn_get(struct uvm_object *uobj, voff_t offset,
*/
int
uvn_findpages(struct uvm_object *uobj, voff_t offset, int *npagesp,
struct vm_page **pgs, int flags)
uvn_findpages(struct uvm_object *uobj, voff_t offset, unsigned int *npagesp,
struct vm_page **pgs, struct uvm_page_array *a, unsigned int flags)
{
int i, count, found, npages, rv;
unsigned int count, found, npages;
int i, rv;
struct uvm_page_array a_store;
if (a == NULL) {
a = &a_store;
uvm_page_array_init(a);
}
count = found = 0;
npages = *npagesp;
if (flags & UFP_BACKWARD) {
for (i = npages - 1; i >= 0; i--, offset -= PAGE_SIZE) {
rv = uvn_findpage(uobj, offset, &pgs[i], flags);
rv = uvn_findpage(uobj, offset, &pgs[i], flags, a,
i + 1);
if (rv == 0) {
if (flags & UFP_DIRTYONLY)
break;
@ -220,7 +228,8 @@ uvn_findpages(struct uvm_object *uobj, voff_t offset, int *npagesp,
}
} else {
for (i = 0; i < npages; i++, offset += PAGE_SIZE) {
rv = uvn_findpage(uobj, offset, &pgs[i], flags);
rv = uvn_findpage(uobj, offset, &pgs[i], flags, a,
npages - i);
if (rv == 0) {
if (flags & UFP_DIRTYONLY)
break;
@ -229,16 +238,29 @@ uvn_findpages(struct uvm_object *uobj, voff_t offset, int *npagesp,
count++;
}
}
if (a == &a_store) {
uvm_page_array_fini(a);
}
*npagesp = count;
return (found);
}
/*
* uvn_findpage: find a single page
*
* if a suitable page was found, put it in *pgp and return 1.
* otherwise return 0.
*/
static int
uvn_findpage(struct uvm_object *uobj, voff_t offset, struct vm_page **pgp,
int flags)
unsigned int flags, struct uvm_page_array *a, unsigned int nleft)
{
struct vm_page *pg;
bool dirty;
const unsigned int fillflags =
((flags & UFP_BACKWARD) ? UVM_PAGE_ARRAY_FILL_BACKWARD : 0) |
((flags & UFP_DIRTYONLY) ?
(UVM_PAGE_ARRAY_FILL_DIRTY|UVM_PAGE_ARRAY_FILL_DENSE) : 0);
UVMHIST_FUNC("uvn_findpage"); UVMHIST_CALLED(ubchist);
UVMHIST_LOG(ubchist, "vp %#jx off 0x%jx", (uintptr_t)uobj, offset,
0, 0);
@ -247,11 +269,35 @@ uvn_findpage(struct uvm_object *uobj, voff_t offset, struct vm_page **pgp,
if (*pgp != NULL) {
UVMHIST_LOG(ubchist, "dontcare", 0,0,0,0);
return 0;
goto skip_offset;
}
for (;;) {
/* look for an existing page */
pg = uvm_pagelookup(uobj, offset);
/*
* look for an existing page.
*
* XXX fragile API
* note that the array can be the one supplied by the caller of
* uvn_findpages. in that case, fillflags used by the caller
* might not match strictly with ours.
* in particular, the caller might have filled the array
* without DENSE but passed us UFP_DIRTYONLY (thus DENSE).
*/
pg = uvm_page_array_fill_and_peek(a, uobj, offset, nleft,
fillflags);
if (pg != NULL && pg->offset != offset) {
KASSERT(
((fillflags & UVM_PAGE_ARRAY_FILL_BACKWARD) != 0)
== (pg->offset < offset));
KASSERT(uvm_pagelookup(uobj, offset) == NULL
|| ((fillflags & UVM_PAGE_ARRAY_FILL_DIRTY) != 0 &&
radix_tree_get_tag(&uobj->uo_pages,
offset >> PAGE_SHIFT, UVM_PAGE_DIRTY_TAG) == 0));
pg = NULL;
if ((fillflags & UVM_PAGE_ARRAY_FILL_DENSE) != 0) {
UVMHIST_LOG(ubchist, "dense", 0,0,0,0);
return 0;
}
}
/* nope? allocate one now */
if (pg == NULL) {
@ -268,28 +314,32 @@ uvn_findpage(struct uvm_object *uobj, voff_t offset, struct vm_page **pgp,
}
mutex_exit(uobj->vmobjlock);
uvm_wait("uvn_fp1");
uvm_page_array_clear(a);
mutex_enter(uobj->vmobjlock);
continue;
}
UVMHIST_LOG(ubchist, "alloced %#jx (color %ju)",
(uintptr_t)pg, VM_PGCOLOR(pg), 0, 0);
KASSERTMSG(uvm_pagegetdirty(pg) ==
UVM_PAGE_STATUS_CLEAN, "page %p not clean", pg);
break;
} else if (flags & UFP_NOCACHE) {
UVMHIST_LOG(ubchist, "nocache",0,0,0,0);
return 0;
goto skip;
}
/* page is there, see if we need to wait on it */
if ((pg->flags & PG_BUSY) != 0) {
if (flags & UFP_NOWAIT) {
UVMHIST_LOG(ubchist, "nowait",0,0,0,0);
return 0;
goto skip;
}
pg->flags |= PG_WANTED;
UVMHIST_LOG(ubchist, "wait %#jx (color %ju)",
(uintptr_t)pg, VM_PGCOLOR(pg), 0, 0);
UVM_UNLOCK_AND_WAIT(pg, uobj->vmobjlock, 0,
"uvn_fp2", 0);
uvm_page_array_clear(a);
mutex_enter(uobj->vmobjlock);
continue;
}
@ -297,14 +347,12 @@ uvn_findpage(struct uvm_object *uobj, voff_t offset, struct vm_page **pgp,
/* skip PG_RDONLY pages if requested */
if ((flags & UFP_NORDONLY) && (pg->flags & PG_RDONLY)) {
UVMHIST_LOG(ubchist, "nordonly",0,0,0,0);
return 0;
goto skip;
}
/* stop on clean pages if requested */
if (flags & UFP_DIRTYONLY) {
dirty = pmap_clear_modify(pg) ||
(pg->flags & PG_CLEAN) == 0;
pg->flags |= PG_CLEAN;
const bool dirty = uvm_pagecheckdirty(pg, false);
if (!dirty) {
UVMHIST_LOG(ubchist, "dirtonly", 0,0,0,0);
return 0;
@ -316,10 +364,33 @@ uvn_findpage(struct uvm_object *uobj, voff_t offset, struct vm_page **pgp,
UVM_PAGE_OWN(pg, "uvn_findpage");
UVMHIST_LOG(ubchist, "found %#jx (color %ju)",
(uintptr_t)pg, VM_PGCOLOR(pg), 0, 0);
uvm_page_array_advance(a);
break;
}
*pgp = pg;
return 1;
skip_offset:
/*
* skip this offset
*/
pg = uvm_page_array_peek(a);
if (pg != NULL) {
if (pg->offset == offset) {
uvm_page_array_advance(a);
} else {
KASSERT((fillflags & UVM_PAGE_ARRAY_FILL_DENSE) == 0);
}
}
return 0;
skip:
/*
* skip this page
*/
KASSERT(pg != NULL);
uvm_page_array_advance(a);
return 0;
}
/*