Module Name: src
Committed By: ad
Date: Wed Jan 15 17:55:45 UTC 2020
Modified Files:
src/external/cddl/osnet/dist/uts/common/fs/zfs: zfs_vnops.c
src/sys/external/bsd/drm2/dist/drm: drm_gem.c
src/sys/external/bsd/drm2/dist/drm/i915: i915_gem.c i915_gem_fence.c
src/sys/external/bsd/drm2/include/linux: mm.h
src/sys/miscfs/genfs: genfs_io.c genfs_node.h
src/sys/nfs: nfs_bio.c
src/sys/rump/librump/rumpkern: Makefile.rumpkern vm.c
src/sys/rump/librump/rumpvfs: vm_vfs.c
src/sys/sys: cpu_data.h
src/sys/ufs/lfs: lfs_pages.c lfs_segment.c lfs_vfsops.c ulfs_inode.c
src/sys/ufs/ufs: ufs_inode.c
src/sys/uvm: files.uvm uvm_anon.c uvm_aobj.c uvm_bio.c uvm_extern.h
uvm_fault.c uvm_loan.c uvm_meter.c uvm_object.c uvm_object.h
uvm_page.c uvm_page.h uvm_page_array.c uvm_pager.c uvm_pdaemon.c
uvm_vnode.c
Added Files:
src/sys/uvm: uvm_page_status.c
Log Message:
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.
To generate a diff of this commit:
cvs rdiff -u -r1.54 -r1.55 \
src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c
cvs rdiff -u -r1.10 -r1.11 src/sys/external/bsd/drm2/dist/drm/drm_gem.c
cvs rdiff -u -r1.54 -r1.55 src/sys/external/bsd/drm2/dist/drm/i915/i915_gem.c
cvs rdiff -u -r1.5 -r1.6 \
src/sys/external/bsd/drm2/dist/drm/i915/i915_gem_fence.c
cvs rdiff -u -r1.9 -r1.10 src/sys/external/bsd/drm2/include/linux/mm.h
cvs rdiff -u -r1.83 -r1.84 src/sys/miscfs/genfs/genfs_io.c
cvs rdiff -u -r1.22 -r1.23 src/sys/miscfs/genfs/genfs_node.h
cvs rdiff -u -r1.192 -r1.193 src/sys/nfs/nfs_bio.c
cvs rdiff -u -r1.181 -r1.182 src/sys/rump/librump/rumpkern/Makefile.rumpkern
cvs rdiff -u -r1.182 -r1.183 src/sys/rump/librump/rumpkern/vm.c
cvs rdiff -u -r1.35 -r1.36 src/sys/rump/librump/rumpvfs/vm_vfs.c
cvs rdiff -u -r1.48 -r1.49 src/sys/sys/cpu_data.h
cvs rdiff -u -r1.19 -r1.20 src/sys/ufs/lfs/lfs_pages.c
cvs rdiff -u -r1.280 -r1.281 src/sys/ufs/lfs/lfs_segment.c
cvs rdiff -u -r1.367 -r1.368 src/sys/ufs/lfs/lfs_vfsops.c
cvs rdiff -u -r1.23 -r1.24 src/sys/ufs/lfs/ulfs_inode.c
cvs rdiff -u -r1.107 -r1.108 src/sys/ufs/ufs/ufs_inode.c
cvs rdiff -u -r1.32 -r1.33 src/sys/uvm/files.uvm
cvs rdiff -u -r1.70 -r1.71 src/sys/uvm/uvm_anon.c
cvs rdiff -u -r1.133 -r1.134 src/sys/uvm/uvm_aobj.c
cvs rdiff -u -r1.102 -r1.103 src/sys/uvm/uvm_bio.c
cvs rdiff -u -r1.218 -r1.219 src/sys/uvm/uvm_extern.h
cvs rdiff -u -r1.214 -r1.215 src/sys/uvm/uvm_fault.c
cvs rdiff -u -r1.93 -r1.94 src/sys/uvm/uvm_loan.c
cvs rdiff -u -r1.73 -r1.74 src/sys/uvm/uvm_meter.c
cvs rdiff -u -r1.19 -r1.20 src/sys/uvm/uvm_object.c
cvs rdiff -u -r1.35 -r1.36 src/sys/uvm/uvm_object.h
cvs rdiff -u -r1.223 -r1.224 src/sys/uvm/uvm_page.c
cvs rdiff -u -r1.95 -r1.96 src/sys/uvm/uvm_page.h
cvs rdiff -u -r1.2 -r1.3 src/sys/uvm/uvm_page_array.c
cvs rdiff -u -r0 -r1.2 src/sys/uvm/uvm_page_status.c
cvs rdiff -u -r1.119 -r1.120 src/sys/uvm/uvm_pager.c
cvs rdiff -u -r1.122 -r1.123 src/sys/uvm/uvm_pdaemon.c
cvs rdiff -u -r1.104 -r1.105 src/sys/uvm/uvm_vnode.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c
diff -u src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.54 src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.55
--- src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c:1.54 Fri Dec 13 20:10:21 2019
+++ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c Wed Jan 15 17:55:43 2020
@@ -746,7 +746,8 @@ mappedread(vnode_t *vp, int nbytes, 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,
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 si
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);
Index: src/sys/external/bsd/drm2/dist/drm/drm_gem.c
diff -u src/sys/external/bsd/drm2/dist/drm/drm_gem.c:1.10 src/sys/external/bsd/drm2/dist/drm/drm_gem.c:1.11
--- src/sys/external/bsd/drm2/dist/drm/drm_gem.c:1.10 Mon Aug 27 15:22:53 2018
+++ src/sys/external/bsd/drm2/dist/drm/drm_gem.c Wed Jan 15 17:55:43 2020
@@ -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
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);
Index: src/sys/external/bsd/drm2/dist/drm/i915/i915_gem.c
diff -u src/sys/external/bsd/drm2/dist/drm/i915/i915_gem.c:1.54 src/sys/external/bsd/drm2/dist/drm/i915/i915_gem.c:1.55
--- src/sys/external/bsd/drm2/dist/drm/i915/i915_gem.c:1.54 Mon Aug 27 15:22:54 2018
+++ src/sys/external/bsd/drm2/dist/drm/i915/i915_gem.c Wed Jan 15 17:55:43 2020
@@ -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
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 */
}
}
Index: src/sys/external/bsd/drm2/dist/drm/i915/i915_gem_fence.c
diff -u src/sys/external/bsd/drm2/dist/drm/i915/i915_gem_fence.c:1.5 src/sys/external/bsd/drm2/dist/drm/i915/i915_gem_fence.c:1.6
--- src/sys/external/bsd/drm2/dist/drm/i915/i915_gem_fence.c:1.5 Mon Aug 27 15:09:35 2018
+++ src/sys/external/bsd/drm2/dist/drm/i915/i915_gem_fence.c Wed Jan 15 17:55:43 2020
@@ -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
(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;
}
Index: src/sys/external/bsd/drm2/include/linux/mm.h
diff -u src/sys/external/bsd/drm2/include/linux/mm.h:1.9 src/sys/external/bsd/drm2/include/linux/mm.h:1.10
--- src/sys/external/bsd/drm2/include/linux/mm.h:1.9 Mon Aug 27 13:44:54 2018
+++ src/sys/external/bsd/drm2/include/linux/mm.h Wed Jan 15 17:55:44 2020
@@ -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_ */
Index: src/sys/miscfs/genfs/genfs_io.c
diff -u src/sys/miscfs/genfs/genfs_io.c:1.83 src/sys/miscfs/genfs/genfs_io.c:1.84
--- src/sys/miscfs/genfs/genfs_io.c:1.83 Tue Dec 31 22:42:50 2019
+++ src/sys/miscfs/genfs/genfs_io.c Wed Jan 15 17:55:44 2020
@@ -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, un
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);
@@ -364,41 +379,57 @@ startover:
}
/*
- * if the pages are already resident, just return them.
+ * if PGO_OVERWRITE is set, don't bother reading the pages.
*/
- 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 (overwrite) {
if (!glocked) {
genfs_node_unlock(vp);
}
- UVMHIST_LOG(ubchist, "returning cached pages", 0,0,0,0);
+ UVMHIST_LOG(ubchist, "PGO_OVERWRITE",0,0,0,0);
+
+ for (i = 0; i < npages; i++) {
+ struct vm_page *pg = pgs[ridx + i];
+
+ /*
+ * 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 PGO_OVERWRITE is set, don't bother reading the pages.
+ * if the pages are already resident, just return them.
*/
- if (overwrite) {
+ 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, "PGO_OVERWRITE",0,0,0,0);
-
- for (i = 0; i < npages; i++) {
- struct vm_page *pg = pgs[ridx + i];
-
- pg->flags &= ~(PG_RDONLY|PG_CLEAN);
- }
+ UVMHIST_LOG(ubchist, "returning cached pages", 0,0,0,0);
npages += ridx;
goto out;
}
@@ -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, st
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_
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);
Index: src/sys/miscfs/genfs/genfs_node.h
diff -u src/sys/miscfs/genfs/genfs_node.h:1.22 src/sys/miscfs/genfs/genfs_node.h:1.23
--- src/sys/miscfs/genfs/genfs_node.h:1.22 Mon May 28 21:04:38 2018
+++ src/sys/miscfs/genfs/genfs_node.h Wed Jan 15 17:55:44 2020
@@ -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)
Index: src/sys/nfs/nfs_bio.c
diff -u src/sys/nfs/nfs_bio.c:1.192 src/sys/nfs/nfs_bio.c:1.193
--- src/sys/nfs/nfs_bio.c:1.192 Fri Dec 13 20:10:21 2019
+++ src/sys/nfs/nfs_bio.c Wed Jan 15 17:55:44 2020
@@ -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);
}
Index: src/sys/rump/librump/rumpkern/Makefile.rumpkern
diff -u src/sys/rump/librump/rumpkern/Makefile.rumpkern:1.181 src/sys/rump/librump/rumpkern/Makefile.rumpkern:1.182
--- src/sys/rump/librump/rumpkern/Makefile.rumpkern:1.181 Fri Dec 20 21:20:09 2019
+++ src/sys/rump/librump/rumpkern/Makefile.rumpkern Wed Jan 15 17:55:44 2020
@@ -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
Index: src/sys/rump/librump/rumpkern/vm.c
diff -u src/sys/rump/librump/rumpkern/vm.c:1.182 src/sys/rump/librump/rumpkern/vm.c:1.183
--- src/sys/rump/librump/rumpkern/vm.c:1.182 Sun Jan 5 15:57:15 2020
+++ src/sys/rump/librump/rumpkern/vm.c Wed Jan 15 17:55:44 2020
@@ -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);
}
Index: src/sys/rump/librump/rumpvfs/vm_vfs.c
diff -u src/sys/rump/librump/rumpvfs/vm_vfs.c:1.35 src/sys/rump/librump/rumpvfs/vm_vfs.c:1.36
--- src/sys/rump/librump/rumpvfs/vm_vfs.c:1.35 Fri Dec 13 20:10:22 2019
+++ src/sys/rump/librump/rumpvfs/vm_vfs.c Wed Jan 15 17:55:44 2020
@@ -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, o
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, str
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);
Index: src/sys/sys/cpu_data.h
diff -u src/sys/sys/cpu_data.h:1.48 src/sys/sys/cpu_data.h:1.49
--- src/sys/sys/cpu_data.h:1.48 Sun Jan 12 13:29:24 2020
+++ src/sys/sys/cpu_data.h Wed Jan 15 17:55:44 2020
@@ -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 */
};
/*
Index: src/sys/ufs/lfs/lfs_pages.c
diff -u src/sys/ufs/lfs/lfs_pages.c:1.19 src/sys/ufs/lfs/lfs_pages.c:1.20
--- src/sys/ufs/lfs/lfs_pages.c:1.19 Tue Dec 31 22:42:51 2019
+++ src/sys/ufs/lfs/lfs_pages.c Wed Jan 15 17:55:44 2020
@@ -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
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
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
Index: src/sys/ufs/lfs/lfs_segment.c
diff -u src/sys/ufs/lfs/lfs_segment.c:1.280 src/sys/ufs/lfs/lfs_segment.c:1.281
--- src/sys/ufs/lfs/lfs_segment.c:1.280 Sun Dec 8 19:52:37 2019
+++ src/sys/ufs/lfs/lfs_segment.c Wed Jan 15 17:55:44 2020
@@ -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,
Index: src/sys/ufs/lfs/lfs_vfsops.c
diff -u src/sys/ufs/lfs/lfs_vfsops.c:1.367 src/sys/ufs/lfs/lfs_vfsops.c:1.368
--- src/sys/ufs/lfs/lfs_vfsops.c:1.367 Tue Dec 31 22:42:51 2019
+++ src/sys/ufs/lfs/lfs_vfsops.c Wed Jan 15 17:55:44 2020
@@ -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 v
}
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));
Index: src/sys/ufs/lfs/ulfs_inode.c
diff -u src/sys/ufs/lfs/ulfs_inode.c:1.23 src/sys/ufs/lfs/ulfs_inode.c:1.24
--- src/sys/ufs/lfs/ulfs_inode.c:1.23 Tue Dec 31 22:42:51 2019
+++ src/sys/ufs/lfs/ulfs_inode.c Wed Jan 15 17:55:44 2020
@@ -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_
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_
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]);
Index: src/sys/ufs/ufs/ufs_inode.c
diff -u src/sys/ufs/ufs/ufs_inode.c:1.107 src/sys/ufs/ufs/ufs_inode.c:1.108
--- src/sys/ufs/ufs/ufs_inode.c:1.107 Tue Dec 31 22:42:51 2019
+++ src/sys/ufs/ufs/ufs_inode.c Wed Jan 15 17:55:44 2020
@@ -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
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
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]);
Index: src/sys/uvm/files.uvm
diff -u src/sys/uvm/files.uvm:1.32 src/sys/uvm/files.uvm:1.33
--- src/sys/uvm/files.uvm:1.32 Fri Dec 27 12:51:57 2019
+++ src/sys/uvm/files.uvm Wed Jan 15 17:55:45 2020
@@ -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
Index: src/sys/uvm/uvm_anon.c
diff -u src/sys/uvm/uvm_anon.c:1.70 src/sys/uvm/uvm_anon.c:1.71
--- src/sys/uvm/uvm_anon.c:1.70 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_anon.c Wed Jan 15 17:55:45 2020
@@ -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, st
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).
Index: src/sys/uvm/uvm_aobj.c
diff -u src/sys/uvm/uvm_aobj.c:1.133 src/sys/uvm/uvm_aobj.c:1.134
--- src/sys/uvm/uvm_aobj.c:1.133 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_aobj.c Wed Jan 15 17:55:45 2020
@@ -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
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
* 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, i
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;
Index: src/sys/uvm/uvm_bio.c
diff -u src/sys/uvm/uvm_bio.c:1.102 src/sys/uvm/uvm_bio.c:1.103
--- src/sys/uvm/uvm_bio.c:1.102 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_bio.c Wed Jan 15 17:55:45 2020
@@ -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_faultinf
/*
* 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_faultinf
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 *uo
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);
Index: src/sys/uvm/uvm_extern.h
diff -u src/sys/uvm/uvm_extern.h:1.218 src/sys/uvm/uvm_extern.h:1.219
--- src/sys/uvm/uvm_extern.h:1.218 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_extern.h Wed Jan 15 17:55:45 2020
@@ -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 *);
Index: src/sys/uvm/uvm_fault.c
diff -u src/sys/uvm/uvm_fault.c:1.214 src/sys/uvm/uvm_fault.c:1.215
--- src/sys/uvm/uvm_fault.c:1.214 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_fault.c Wed Jan 15 17:55:45 2020
@@ -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 *u
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 *u
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_looku
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 {
Index: src/sys/uvm/uvm_loan.c
diff -u src/sys/uvm/uvm_loan.c:1.93 src/sys/uvm/uvm_loan.c:1.94
--- src/sys/uvm/uvm_loan.c:1.93 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_loan.c Wed Jan 15 17:55:45 2020
@@ -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,
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,
}
/* 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;
}
Index: src/sys/uvm/uvm_meter.c
diff -u src/sys/uvm/uvm_meter.c:1.73 src/sys/uvm/uvm_meter.c:1.74
--- src/sys/uvm/uvm_meter.c:1.73 Tue Dec 31 13:07:14 2019
+++ src/sys/uvm/uvm_meter.c Wed Jan 15 17:55:45 2020
@@ -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;
Index: src/sys/uvm/uvm_object.c
diff -u src/sys/uvm/uvm_object.c:1.19 src/sys/uvm/uvm_object.c:1.20
--- src/sys/uvm/uvm_object.c:1.19 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_object.c Wed Jan 15 17:55:45 2020
@@ -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 *uob
}
if (pgs[i]->flags & PG_AOBJ) {
- pgs[i]->flags &= ~(PG_CLEAN);
+ uvm_pagemarkdirty(pgs[i],
+ UVM_PAGE_STATUS_DIRTY);
uao_dropswap(uobj, i);
}
}
Index: src/sys/uvm/uvm_object.h
diff -u src/sys/uvm/uvm_object.h:1.35 src/sys/uvm/uvm_object.h:1.36
--- src/sys/uvm/uvm_object.h:1.35 Sun Dec 15 21:11:35 2019
+++ src/sys/uvm/uvm_object.h Wed Jan 15 17:55:45 2020
@@ -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.
@@ -63,6 +63,13 @@ struct uvm_object {
};
/*
+ * 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
* memory objects don't have reference counts -- they never die).
Index: src/sys/uvm/uvm_page.c
diff -u src/sys/uvm/uvm_page.c:1.223 src/sys/uvm/uvm_page.c:1.224
--- src/sys/uvm/uvm_page.c:1.223 Sat Jan 11 19:51:01 2020
+++ src/sys/uvm/uvm_page.c Wed Jan 15 17:55:45 2020
@@ -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
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;
+
+ if (!isaobj) {
+ KASSERT((pg->flags & PG_FILE) != 0);
+ if (uobj->uo_npages == 0) {
+ struct vnode *vp = (struct vnode *)uobj;
- vholdl(vp);
- }
- if (UVM_OBJ_IS_VTEXT(uobj)) {
- cpu_count(CPU_COUNT_EXECPAGES, 1);
+ 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 *u
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
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;
+
+ if (!isaobj) {
+ KASSERT((pg->flags & PG_FILE) != 0);
+ if (uobj->uo_npages == 1) {
+ struct vnode *vp = (struct vnode *)uobj;
- holdrelel(vp);
- }
- if (UVM_OBJ_IS_VTEXT(uobj)) {
- cpu_count(CPU_COUNT_EXECPAGES, -1);
+ 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 *o
}
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 *o
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 *o
* 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, s
{
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, s
KASSERT(mutex_owned(&oldpg->interlock));
KASSERT(mutex_owned(&newpg->interlock));
+ newpg->uobject = uobj;
newpg->offset = oldpg->offset;
- pg = radix_tree_replace_node(&uobj->uo_pages,
- newpg->offset >> PAGE_SHIFT, newpg);
+ idx = newpg->offset >> PAGE_SHIFT;
+ pg = radix_tree_replace_node(&uobj->uo_pages, idx, newpg);
KASSERT(pg == oldpg);
-
- newpg->uobject = uobj;
+ 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, str
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 *
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, boo
(*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);
Index: src/sys/uvm/uvm_page.h
diff -u src/sys/uvm/uvm_page.h:1.95 src/sys/uvm/uvm_page.h:1.96
--- src/sys/uvm/uvm_page.h:1.95 Fri Jan 10 21:32:17 2020
+++ src/sys/uvm/uvm_page.h Wed Jan 15 17:55:45 2020
@@ -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.
+ */
+
+/*
+ * if you want to renumber PG_CLEAN and PG_DIRTY, check __CTASSERTs in
+ * uvm_page_status.c first.
*/
-#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
+#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 *);
@@ -356,6 +370,23 @@ int uvm_direct_process(struct vm_page **
#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
*/
Index: src/sys/uvm/uvm_page_array.c
diff -u src/sys/uvm/uvm_page_array.c:1.2 src/sys/uvm/uvm_page_array.c:1.3
--- src/sys/uvm/uvm_page_array.c:1.2 Sun Dec 15 21:11:35 2019
+++ src/sys/uvm/uvm_page_array.c Wed Jan 15 17:55:45 2020
@@ -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_arra
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_arra
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)(
Index: src/sys/uvm/uvm_pager.c
diff -u src/sys/uvm/uvm_pager.c:1.119 src/sys/uvm/uvm_pager.c:1.120
--- src/sys/uvm/uvm_pager.c:1.119 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_pager.c Wed Jan 15 17:55:45 2020
@@ -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 **p
}
#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 **p
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 **p
/*
* 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 **p
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);
}
/*
Index: src/sys/uvm/uvm_pdaemon.c
diff -u src/sys/uvm/uvm_pdaemon.c:1.122 src/sys/uvm/uvm_pdaemon.c:1.123
--- src/sys/uvm/uvm_pdaemon.c:1.122 Tue Dec 31 22:42:51 2019
+++ src/sys/uvm/uvm_pdaemon.c Wed Jan 15 17:55:45 2020
@@ -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;
Index: src/sys/uvm/uvm_vnode.c
diff -u src/sys/uvm/uvm_vnode.c:1.104 src/sys/uvm/uvm_vnode.c:1.105
--- src/sys/uvm/uvm_vnode.c:1.104 Sat Dec 21 14:41:44 2019
+++ src/sys/uvm/uvm_vnode.c Wed Jan 15 17:55:45 2020
@@ -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,
#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 *,
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
*/
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, v
}
} 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, v
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, vo
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, vo
}
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, vo
/* 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, vo
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;
}
/*
Added files:
Index: src/sys/uvm/uvm_page_status.c
diff -u /dev/null src/sys/uvm/uvm_page_status.c:1.2
--- /dev/null Wed Jan 15 17:55:45 2020
+++ src/sys/uvm/uvm_page_status.c Wed Jan 15 17:55:45 2020
@@ -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;
+}