Module Name: src
Committed By: ad
Date: Sun Dec 15 21:11:35 UTC 2019
Modified Files:
src/sys/arch/hppa/hppa: pmap.c
src/sys/arch/sparc64/include: pmap.h
src/sys/arch/sparc64/sparc64: pmap.c
src/sys/miscfs/genfs: genfs_io.c
src/sys/nfs: nfs_subs.c
src/sys/rump/librump/rumpkern: Makefile.rumpkern vm.c
src/sys/ufs/lfs: lfs_pages.c
src/sys/uvm: files.uvm uvm_aobj.c uvm_loan.c uvm_object.c uvm_object.h
uvm_page.c uvm_page.h
Added Files:
src/sys/uvm: uvm_page_array.c uvm_page_array.h
Log Message:
Merge from yamt-pagecache:
- do gang lookup of pages using radixtree.
- remove now unused uvm_object::uo_memq and vm_page::listq.queue.
To generate a diff of this commit:
cvs rdiff -u -r1.100 -r1.101 src/sys/arch/hppa/hppa/pmap.c
cvs rdiff -u -r1.62 -r1.63 src/sys/arch/sparc64/include/pmap.h
cvs rdiff -u -r1.310 -r1.311 src/sys/arch/sparc64/sparc64/pmap.c
cvs rdiff -u -r1.77 -r1.78 src/sys/miscfs/genfs/genfs_io.c
cvs rdiff -u -r1.235 -r1.236 src/sys/nfs/nfs_subs.c
cvs rdiff -u -r1.178 -r1.179 src/sys/rump/librump/rumpkern/Makefile.rumpkern
cvs rdiff -u -r1.175 -r1.176 src/sys/rump/librump/rumpkern/vm.c
cvs rdiff -u -r1.16 -r1.17 src/sys/ufs/lfs/lfs_pages.c
cvs rdiff -u -r1.30 -r1.31 src/sys/uvm/files.uvm
cvs rdiff -u -r1.131 -r1.132 src/sys/uvm/uvm_aobj.c
cvs rdiff -u -r1.90 -r1.91 src/sys/uvm/uvm_loan.c
cvs rdiff -u -r1.17 -r1.18 src/sys/uvm/uvm_object.c
cvs rdiff -u -r1.34 -r1.35 src/sys/uvm/uvm_object.h
cvs rdiff -u -r1.202 -r1.203 src/sys/uvm/uvm_page.c
cvs rdiff -u -r1.86 -r1.87 src/sys/uvm/uvm_page.h
cvs rdiff -u -r0 -r1.2 src/sys/uvm/uvm_page_array.c \
src/sys/uvm/uvm_page_array.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/hppa/hppa/pmap.c
diff -u src/sys/arch/hppa/hppa/pmap.c:1.100 src/sys/arch/hppa/hppa/pmap.c:1.101
--- src/sys/arch/hppa/hppa/pmap.c:1.100 Thu Dec 22 14:47:57 2016
+++ src/sys/arch/hppa/hppa/pmap.c Sun Dec 15 21:11:34 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: pmap.c,v 1.100 2016/12/22 14:47:57 cherry Exp $ */
+/* $NetBSD: pmap.c,v 1.101 2019/12/15 21:11:34 ad Exp $ */
/*-
* Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
@@ -65,7 +65,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.100 2016/12/22 14:47:57 cherry Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.101 2019/12/15 21:11:34 ad Exp $");
#include "opt_cputype.h"
@@ -76,6 +76,7 @@ __KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.1
#include <sys/mutex.h>
#include <uvm/uvm.h>
+#include <uvm/uvm_page_array.h>
#include <machine/reg.h>
#include <machine/psl.h>
@@ -395,7 +396,7 @@ pmap_pde_release(pmap_t pmap, vaddr_t va
pmap_pde_set(pmap, va, 0);
pmap->pm_stats.resident_count--;
if (pmap->pm_ptphint == ptp)
- pmap->pm_ptphint = TAILQ_FIRST(&pmap->pm_obj.memq);
+ pmap->pm_ptphint = NULL;
ptp->wire_count = 0;
KASSERT((ptp->flags & PG_BUSY) == 0);
@@ -1101,7 +1102,9 @@ void
pmap_destroy(pmap_t pmap)
{
#ifdef DIAGNOSTIC
+ struct uvm_page_array a;
struct vm_page *pg;
+ off_t off;
#endif
int refs;
@@ -1115,12 +1118,18 @@ pmap_destroy(pmap_t pmap)
return;
#ifdef DIAGNOSTIC
- while ((pg = TAILQ_FIRST(&pmap->pm_obj.memq))) {
+ uvm_page_array_init(&a);
+ off = 0;
+ mutex_enter(pmap->pm_lock);
+ while ((pg = uvm_page_array_fill_and_peek(&a, &pmap->pm_obj, off, 0, 0))
+ != NULL) {
pt_entry_t *pde, *epde;
struct vm_page *spg;
struct pv_entry *pv, *npv;
paddr_t pa;
+ off = pg->offset + PAGE_SIZE;
+ uvm_page_array_advance(&a);
KASSERT(pg != pmap->pm_pdir_pg);
pa = VM_PAGE_TO_PHYS(pg);
@@ -1152,6 +1161,8 @@ pmap_destroy(pmap_t pmap)
}
DPRINTF(PDB_FOLLOW, ("\n"));
}
+ mutex_exit(pmap->pm_lock);
+ uvm_page_array_fini(&a);
#endif
pmap_sdir_set(pmap->pm_space, 0);
mutex_enter(pmap->pm_lock);
Index: src/sys/arch/sparc64/include/pmap.h
diff -u src/sys/arch/sparc64/include/pmap.h:1.62 src/sys/arch/sparc64/include/pmap.h:1.63
--- src/sys/arch/sparc64/include/pmap.h:1.62 Thu Jan 10 10:33:49 2019
+++ src/sys/arch/sparc64/include/pmap.h Sun Dec 15 21:11:34 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: pmap.h,v 1.62 2019/01/10 10:33:49 mrg Exp $ */
+/* $NetBSD: pmap.h,v 1.63 2019/12/15 21:11:34 ad Exp $ */
/*-
* Copyright (C) 1995, 1996 Wolfgang Solfrank.
@@ -126,10 +126,8 @@ extern struct page_size_map page_size_ma
#endif
struct pmap {
- struct uvm_object pm_obj;
- kmutex_t pm_obj_lock;
-#define pm_lock pm_obj.vmobjlock
-#define pm_refs pm_obj.uo_refs
+ unsigned int pm_refs;
+ TAILQ_HEAD(, vm_page) pm_ptps;
LIST_ENTRY(pmap) pm_list[PMAP_LIST_MAXNUMCPU]; /* per cpu ctx used list */
struct pmap_statistics pm_stats;
Index: src/sys/arch/sparc64/sparc64/pmap.c
diff -u src/sys/arch/sparc64/sparc64/pmap.c:1.310 src/sys/arch/sparc64/sparc64/pmap.c:1.311
--- src/sys/arch/sparc64/sparc64/pmap.c:1.310 Thu Jan 10 10:33:49 2019
+++ src/sys/arch/sparc64/sparc64/pmap.c Sun Dec 15 21:11:34 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: pmap.c,v 1.310 2019/01/10 10:33:49 mrg Exp $ */
+/* $NetBSD: pmap.c,v 1.311 2019/12/15 21:11:34 ad Exp $ */
/*
*
* Copyright (C) 1996-1999 Eduardo Horvath.
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.310 2019/01/10 10:33:49 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.311 2019/12/15 21:11:34 ad Exp $");
#undef NO_VCACHE /* Don't forget the locked TLB in dostart */
#define HWREF
@@ -1473,10 +1473,8 @@ pmap_create(void)
memset(pm, 0, sizeof *pm);
DPRINTF(PDB_CREATE, ("pmap_create(): created %p\n", pm));
- mutex_init(&pm->pm_obj_lock, MUTEX_DEFAULT, IPL_NONE);
- uvm_obj_init(&pm->pm_obj, NULL, false, 1);
- uvm_obj_setlock(&pm->pm_obj, &pm->pm_obj_lock);
-
+ pm->pm_refs = 1;
+ TAILQ_INIT(&pm->pm_ptps);
if (pm != pmap_kernel()) {
while (!pmap_get_page(&pm->pm_physaddr)) {
uvm_wait("pmap_create");
@@ -1510,7 +1508,7 @@ pmap_destroy(struct pmap *pm)
#else
#define pmap_cpus_active 0
#endif
- struct vm_page *pg, *nextpg;
+ struct vm_page *pg;
if ((int)atomic_dec_uint_nv(&pm->pm_refs) > 0) {
return;
@@ -1538,22 +1536,18 @@ pmap_destroy(struct pmap *pm)
#endif
/* we could be a little smarter and leave pages zeroed */
- for (pg = TAILQ_FIRST(&pm->pm_obj.memq); pg != NULL; pg = nextpg) {
+ while ((pg = TAILQ_FIRST(&pm->pm_ptps)) != NULL) {
#ifdef DIAGNOSTIC
struct vm_page_md *md = VM_PAGE_TO_MD(pg);
#endif
- KASSERT((pg->flags & PG_MARKER) == 0);
- nextpg = TAILQ_NEXT(pg, listq.queue);
- TAILQ_REMOVE(&pm->pm_obj.memq, pg, listq.queue);
+ TAILQ_REMOVE(&pm->pm_ptps, pg, pageq.queue);
KASSERT(md->mdpg_pvh.pv_pmap == NULL);
dcache_flush_page_cpuset(VM_PAGE_TO_PHYS(pg), pmap_cpus_active);
uvm_pagefree(pg);
}
pmap_free_page((paddr_t)(u_long)pm->pm_segs, pmap_cpus_active);
- uvm_obj_destroy(&pm->pm_obj, false);
- mutex_destroy(&pm->pm_obj_lock);
pool_cache_put(&pmap_cache, pm);
}
@@ -1904,7 +1898,7 @@ pmap_enter(struct pmap *pm, vaddr_t va,
ptpg = PHYS_TO_VM_PAGE(ptp);
if (ptpg) {
ptpg->offset = (uint64_t)va & (0xfffffLL << 23);
- TAILQ_INSERT_TAIL(&pm->pm_obj.memq, ptpg, listq.queue);
+ TAILQ_INSERT_TAIL(&pm->pm_ptps, ptpg, pageq.queue);
} else {
KASSERT(pm == pmap_kernel());
}
@@ -1916,7 +1910,7 @@ pmap_enter(struct pmap *pm, vaddr_t va,
ptpg = PHYS_TO_VM_PAGE(ptp);
if (ptpg) {
ptpg->offset = (((uint64_t)va >> 43) & 0x3ffLL) << 13;
- TAILQ_INSERT_TAIL(&pm->pm_obj.memq, ptpg, listq.queue);
+ TAILQ_INSERT_TAIL(&pm->pm_ptps, ptpg, pageq.queue);
} else {
KASSERT(pm == pmap_kernel());
}
Index: src/sys/miscfs/genfs/genfs_io.c
diff -u src/sys/miscfs/genfs/genfs_io.c:1.77 src/sys/miscfs/genfs/genfs_io.c:1.78
--- src/sys/miscfs/genfs/genfs_io.c:1.77 Fri Dec 13 20:10:21 2019
+++ src/sys/miscfs/genfs/genfs_io.c Sun Dec 15 21:11:34 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: genfs_io.c,v 1.77 2019/12/13 20:10:21 ad Exp $ */
+/* $NetBSD: genfs_io.c,v 1.78 2019/12/15 21:11:34 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.77 2019/12/13 20:10:21 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: genfs_io.c,v 1.78 2019/12/15 21:11:34 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: genfs_io.c,v
#include <uvm/uvm.h>
#include <uvm/uvm_pager.h>
+#include <uvm/uvm_page_array.h>
static int genfs_do_directio(struct vmspace *, vaddr_t, size_t, struct vnode *,
off_t, enum uio_rw);
@@ -785,13 +786,6 @@ loopdone:
* (e.g. vm_map) before calling flush.
* => if neither PGO_CLEANIT nor PGO_SYNCIO is set, we will not block
* => if PGO_ALLPAGES is set, then all pages in the object will be processed.
- * => NOTE: we rely on the fact that the object's memq is a TAILQ and
- * that new pages are inserted on the tail end of the list. thus,
- * we can make a complete pass through the object in one go by starting
- * at the head and working towards the tail (new pages are put in
- * front of us).
- * => NOTE: we are allowed to lock the page queues, so the caller
- * must not be holding the page queue lock.
*
* note on "cleaning" object and PG_BUSY pages:
* this routine is holding the lock on the object. the only time
@@ -806,19 +800,6 @@ loopdone:
* 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
* page is PG_BUSY it must stay in its object until it is un-busyed.
- *
- * note on page traversal:
- * we can traverse the pages in an object either by going down the
- * linked list in "uobj->memq", or we can go over the address range
- * by page doing hash table lookups for each address. depending
- * on how many pages are in the object it may be cheaper to do one
- * or the other. we set "by_list" to true if we are using memq.
- * if the cost of a hash lookup was equal to the cost of the list
- * traversal we could compare the number of pages in the start->stop
- * range to the total number of pages in the object. however, it
- * seems that a hash table lookup is more expensive than the linked
- * list traversal, so we multiply the number of pages in the
- * range by an estimate of the relatively higher cost of the hash lookup.
*/
int
@@ -841,7 +822,7 @@ genfs_do_putpages(struct vnode *vp, off_
{
struct uvm_object * const uobj = &vp->v_uobj;
kmutex_t * const slock = uobj->vmobjlock;
- off_t off;
+ off_t nextoff;
int i, error, npages, nback;
int freeflag;
/*
@@ -850,8 +831,9 @@ genfs_do_putpages(struct vnode *vp, off_
*/
struct vm_page *pgs[MAXPHYS / MIN_PAGE_SIZE];
#define MAXPAGES (MAXPHYS / PAGE_SIZE)
- struct vm_page *pg, *nextpg, *tpg, curmp, endmp;
- bool wasclean, by_list, needs_clean, yld;
+ struct vm_page *pg, *tpg;
+ struct uvm_page_array a;
+ bool wasclean, needs_clean;
bool async = (origflags & PGO_SYNCIO) == 0;
bool pagedaemon = curlwp == uvm.pagedaemon_lwp;
struct lwp * const l = curlwp ? curlwp : &lwp0;
@@ -944,12 +926,10 @@ retry:
error = 0;
wasclean = (vp->v_numoutput == 0);
- off = startoff;
+ nextoff = startoff;
if (endoff == 0 || flags & PGO_ALLPAGES) {
endoff = trunc_page(LLONG_MAX);
}
- by_list = (uobj->uo_npages <=
- ((endoff - startoff) >> PAGE_SHIFT) * UVM_PAGE_TREE_PENALTY);
/*
* if this vnode is known not to have dirty pages,
@@ -966,10 +946,7 @@ retry:
}
/*
- * start the loop. when scanning by list, hold the last page
- * in the list before we start. pages allocated after we start
- * will be added to the end of the list, so we can stop at the
- * current last page.
+ * start the loop to scan pages.
*/
cleanall = (flags & PGO_CLEANIT) != 0 && wasclean &&
@@ -977,51 +954,48 @@ retry:
(vp->v_iflag & VI_ONWORKLST) != 0;
dirtygen = gp->g_dirtygen;
freeflag = pagedaemon ? PG_PAGEOUT : PG_RELEASED;
- if (by_list) {
- curmp.flags = PG_MARKER;
- endmp.flags = PG_MARKER;
- pg = TAILQ_FIRST(&uobj->memq);
- TAILQ_INSERT_TAIL(&uobj->memq, &endmp, listq.queue);
- } else {
- pg = uvm_pagelookup(uobj, off);
- }
- nextpg = NULL;
- while (by_list || off < endoff) {
-
+ uvm_page_array_init(&a);
+ for (;;) {
/*
* if the current page is not interesting, move on to the next.
*/
- KASSERT(pg == NULL || (pg->flags & PG_MARKER) != 0 ||
- pg->uobject == uobj);
- KASSERT(pg == NULL ||
- (pg->flags & (PG_RELEASED|PG_PAGEOUT)) == 0 ||
- (pg->flags & (PG_BUSY|PG_MARKER)) != 0);
- if (by_list) {
- if (pg == &endmp) {
- break;
- }
- if (pg->flags & PG_MARKER) {
- pg = TAILQ_NEXT(pg, listq.queue);
- continue;
- }
- if (pg->offset < startoff || pg->offset >= endoff ||
- pg->flags & (PG_RELEASED|PG_PAGEOUT)) {
- if (pg->flags & (PG_RELEASED|PG_PAGEOUT)) {
- wasclean = false;
- }
- pg = TAILQ_NEXT(pg, listq.queue);
- continue;
- }
- off = pg->offset;
- } else if (pg == NULL || pg->flags & (PG_RELEASED|PG_PAGEOUT)) {
- if (pg != NULL) {
- wasclean = false;
- }
- off += PAGE_SIZE;
- if (off < endoff) {
- pg = uvm_pagelookup(uobj, off);
- }
+ pg = uvm_page_array_fill_and_peek(&a, uobj, nextoff, 0, 0);
+ if (pg == NULL) {
+ break;
+ }
+
+ KASSERT(pg->uobject == uobj);
+ KASSERT((pg->flags & (PG_RELEASED|PG_PAGEOUT)) == 0 ||
+ (pg->flags & (PG_BUSY)) != 0);
+ KASSERT(pg->offset >= startoff);
+ KASSERT(pg->offset >= nextoff);
+
+ if (pg->offset >= endoff) {
+ break;
+ }
+
+ if (pg->flags & (PG_RELEASED|PG_PAGEOUT)) {
+ wasclean = false;
+ nextoff = pg->offset + PAGE_SIZE;
+ continue;
+ }
+
+ /*
+ * a preempt point.
+ */
+
+ if ((l->l_cpu->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
+ != 0) {
+ nextoff = pg->offset; /* visit this page again */
+ mutex_exit(slock);
+ preempt();
+ /*
+ * as we dropped the object lock, our cached pages can
+ * be stale.
+ */
+ uvm_page_array_clear(&a);
+ mutex_enter(slock);
continue;
}
@@ -1030,9 +1004,7 @@ retry:
* wait for it to become unbusy.
*/
- yld = (l->l_cpu->ci_schedstate.spc_flags &
- SPCF_SHOULDYIELD) && !pagedaemon;
- if (pg->flags & PG_BUSY || yld) {
+ if (pg->flags & PG_BUSY) {
UVMHIST_LOG(ubchist, "busy %#jx", (uintptr_t)pg,
0, 0, 0);
if (flags & PGO_BUSYFAIL && pg->flags & PG_BUSY) {
@@ -1050,33 +1022,21 @@ retry:
*/
break;
}
- if (by_list) {
- TAILQ_INSERT_BEFORE(pg, &curmp, listq.queue);
- UVMHIST_LOG(ubchist, "curmp next %#jx",
- (uintptr_t)TAILQ_NEXT(&curmp, listq.queue),
- 0, 0, 0);
- }
- if (yld) {
- mutex_exit(slock);
- preempt();
- mutex_enter(slock);
- } else {
- pg->flags |= PG_WANTED;
- UVM_UNLOCK_AND_WAIT(pg, slock, 0, "genput", 0);
- mutex_enter(slock);
- }
- if (by_list) {
- UVMHIST_LOG(ubchist, "after next %#jx",
- (uintptr_t)TAILQ_NEXT(&curmp, listq.queue),
- 0, 0, 0);
- pg = TAILQ_NEXT(&curmp, listq.queue);
- TAILQ_REMOVE(&uobj->memq, &curmp, listq.queue);
- } else {
- pg = uvm_pagelookup(uobj, off);
- }
+ nextoff = pg->offset; /* visit this page again */
+ pg->flags |= PG_WANTED;
+ UVM_UNLOCK_AND_WAIT(pg, slock, 0, "genput", 0);
+ /*
+ * as we dropped the object lock, our cached pages can
+ * be stale.
+ */
+ uvm_page_array_clear(&a);
+ mutex_enter(slock);
continue;
}
+ nextoff = pg->offset + PAGE_SIZE;
+ uvm_page_array_advance(&a);
+
/*
* if we're freeing, remove all mappings of the page now.
* if we're cleaning, check if the page is needs to be cleaned.
@@ -1136,7 +1096,7 @@ retry:
* it fits in the "pgs" pages array.
*/
- off_t fslo, fshi, genlo, lo;
+ off_t fslo, fshi, genlo, lo, off = pg->offset;
GOP_PUTRANGE(vp, off, &fslo, &fshi);
KASSERT(fslo == trunc_page(fslo));
KASSERT(fslo <= off);
@@ -1200,8 +1160,6 @@ retry:
for (i = 0; i < npages; i++) {
tpg = pgs[i];
KASSERT(tpg->uobject == uobj);
- if (by_list && tpg == TAILQ_NEXT(pg, listq.queue))
- pg = tpg;
if (tpg->offset < startoff || tpg->offset >= endoff)
continue;
if (flags & PGO_DEACTIVATE && tpg->wire_count == 0) {
@@ -1222,7 +1180,11 @@ retry:
* and needs_clean is false.
*/
- nextpg = TAILQ_NEXT(tpg, listq.queue);
+ KASSERT(npages == 1);
+ KASSERT(!needs_clean);
+ KASSERT(pg == tpg);
+ KASSERT(nextoff ==
+ tpg->offset + PAGE_SIZE);
uvm_pagefree(tpg);
if (pagedaemon)
uvmexp.pdfreed++;
@@ -1231,52 +1193,29 @@ retry:
}
if (needs_clean) {
modified = true;
+ KASSERT(nextoff == pg->offset + PAGE_SIZE);
+ KASSERT(nback < npages);
+ nextoff = pg->offset + ((npages - nback) << PAGE_SHIFT);
+ KASSERT(pgs[nback] == pg);
+ KASSERT(nextoff == pgs[npages - 1]->offset + PAGE_SIZE);
/*
- * start the i/o. if we're traversing by list,
- * keep our place in the list with a marker page.
+ * start the i/o.
*/
-
- if (by_list) {
- TAILQ_INSERT_AFTER(&uobj->memq, pg, &curmp,
- listq.queue);
- }
mutex_exit(slock);
error = GOP_WRITE(vp, pgs, npages, flags);
+ /*
+ * as we dropped the object lock, our cached pages can
+ * be stale.
+ */
+ uvm_page_array_clear(&a);
mutex_enter(slock);
- if (by_list) {
- pg = TAILQ_NEXT(&curmp, listq.queue);
- TAILQ_REMOVE(&uobj->memq, &curmp, listq.queue);
- }
if (error) {
break;
}
- if (by_list) {
- continue;
- }
- }
-
- /*
- * find the next page and continue if there was no error.
- */
-
- if (by_list) {
- if (nextpg) {
- pg = nextpg;
- nextpg = NULL;
- } else {
- pg = TAILQ_NEXT(pg, listq.queue);
- }
- } else {
- off += (npages - nback) << PAGE_SHIFT;
- if (off < endoff) {
- pg = uvm_pagelookup(uobj, off);
- }
}
}
- if (by_list) {
- TAILQ_REMOVE(&uobj->memq, &endmp, listq.queue);
- }
+ uvm_page_array_fini(&a);
if (modified && (vp->v_iflag & VI_WRMAPDIRTY) != 0 &&
(vp->v_type != VBLK ||
@@ -1377,8 +1316,17 @@ genfs_gop_write(struct vnode *vp, struct
return error;
}
+/*
+ * genfs_gop_write_rwmap:
+ *
+ * a variant of genfs_gop_write. it's used by UDF for its directory buffers.
+ * this maps pages with PROT_WRITE so that VOP_STRATEGY can modifies
+ * the contents before writing it out to the underlying storage.
+ */
+
int
-genfs_gop_write_rwmap(struct vnode *vp, struct vm_page **pgs, int npages, int flags)
+genfs_gop_write_rwmap(struct vnode *vp, struct vm_page **pgs, int npages,
+ int flags)
{
off_t off;
vaddr_t kva;
@@ -1441,6 +1389,10 @@ genfs_do_io(struct vnode *vp, off_t off,
KASSERT(bytes != 0);
if (iowrite) {
+ /*
+ * why += 2?
+ * 1 for biodone, 1 for uvm_aio_aiodone.
+ */
mutex_enter(vp->v_interlock);
vp->v_numoutput += 2;
mutex_exit(vp->v_interlock);
Index: src/sys/nfs/nfs_subs.c
diff -u src/sys/nfs/nfs_subs.c:1.235 src/sys/nfs/nfs_subs.c:1.236
--- src/sys/nfs/nfs_subs.c:1.235 Sat Dec 22 14:28:57 2018
+++ src/sys/nfs/nfs_subs.c Sun Dec 15 21:11:34 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: nfs_subs.c,v 1.235 2018/12/22 14:28:57 maxv Exp $ */
+/* $NetBSD: nfs_subs.c,v 1.236 2019/12/15 21:11:34 ad Exp $ */
/*
* Copyright (c) 1989, 1993
@@ -70,7 +70,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_subs.c,v 1.235 2018/12/22 14:28:57 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_subs.c,v 1.236 2019/12/15 21:11:34 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_nfs.h"
@@ -101,6 +101,7 @@ __KERNEL_RCSID(0, "$NetBSD: nfs_subs.c,v
#include <sys/cprng.h>
#include <uvm/uvm.h>
+#include <uvm/uvm_page_array.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
@@ -1751,6 +1752,8 @@ nfs_clearcommit_selector(void *cl, struc
struct nfs_clearcommit_ctx *c = cl;
struct nfsnode *np;
struct vm_page *pg;
+ struct uvm_page_array a;
+ voff_t off;
KASSERT(mutex_owned(vp->v_interlock));
@@ -1761,9 +1764,15 @@ nfs_clearcommit_selector(void *cl, struc
np->n_pushedhi = 0;
np->n_commitflags &=
~(NFS_COMMIT_PUSH_VALID | NFS_COMMIT_PUSHED_VALID);
- TAILQ_FOREACH(pg, &vp->v_uobj.memq, listq.queue) {
+ uvm_page_array_init(&a);
+ off = 0;
+ while ((pg = uvm_page_array_fill_and_peek(&a, &vp->v_uobj, off,
+ 0, 0)) != NULL) {
pg->flags &= ~PG_NEEDCOMMIT;
+ uvm_page_array_advance(&a);
+ off = pg->offset + PAGE_SIZE;
}
+ uvm_page_array_fini(&a);
return false;
}
Index: src/sys/rump/librump/rumpkern/Makefile.rumpkern
diff -u src/sys/rump/librump/rumpkern/Makefile.rumpkern:1.178 src/sys/rump/librump/rumpkern/Makefile.rumpkern:1.179
--- src/sys/rump/librump/rumpkern/Makefile.rumpkern:1.178 Thu Dec 12 22:55:20 2019
+++ src/sys/rump/librump/rumpkern/Makefile.rumpkern Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile.rumpkern,v 1.178 2019/12/12 22:55:20 pgoyette Exp $
+# $NetBSD: Makefile.rumpkern,v 1.179 2019/12/15 21:11:35 ad Exp $
#
IOCONFDIR:= ${.PARSEDIR}
@@ -138,6 +138,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
# 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.175 src/sys/rump/librump/rumpkern/vm.c:1.176
--- src/sys/rump/librump/rumpkern/vm.c:1.175 Sat Dec 14 17:28:58 2019
+++ src/sys/rump/librump/rumpkern/vm.c Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: vm.c,v 1.175 2019/12/14 17:28:58 ad Exp $ */
+/* $NetBSD: vm.c,v 1.176 2019/12/15 21:11:35 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.175 2019/12/14 17:28:58 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vm.c,v 1.176 2019/12/15 21:11:35 ad Exp $");
#include <sys/param.h>
#include <sys/atomic.h>
@@ -176,7 +176,6 @@ uvm_pagealloc_strat(struct uvm_object *u
uvm_pagezero(pg);
}
- TAILQ_INSERT_TAIL(&uobj->memq, pg, listq.queue);
if (radix_tree_insert_node(&uobj->uo_pages, off >> PAGE_SHIFT,
pg) != 0) {
pool_cache_put(&pagecache, pg);
@@ -216,8 +215,6 @@ uvm_pagefree(struct vm_page *pg)
if (pg->flags & PG_WANTED)
wakeup(pg);
- TAILQ_REMOVE(&uobj->memq, pg, listq.queue);
-
uobj->uo_npages--;
pg2 = radix_tree_remove_node(&uobj->uo_pages, pg->offset >> PAGE_SHIFT);
KASSERT(pg == pg2);
Index: src/sys/ufs/lfs/lfs_pages.c
diff -u src/sys/ufs/lfs/lfs_pages.c:1.16 src/sys/ufs/lfs/lfs_pages.c:1.17
--- src/sys/ufs/lfs/lfs_pages.c:1.16 Fri Dec 13 20:10:22 2019
+++ src/sys/ufs/lfs/lfs_pages.c Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: lfs_pages.c,v 1.16 2019/12/13 20:10:22 ad Exp $ */
+/* $NetBSD: lfs_pages.c,v 1.17 2019/12/15 21:11:35 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.16 2019/12/13 20:10:22 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: lfs_pages.c,v 1.17 2019/12/15 21:11:35 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_compat_netbsd.h"
@@ -242,8 +242,6 @@ check_dirty(struct lfs *fs, struct vnode
off_t startoffset, off_t endoffset, off_t blkeof,
int flags, int checkfirst, struct vm_page **pgp)
{
- int by_list;
- struct vm_page *curpg = NULL; /* XXX: gcc */
struct vm_page *pgs[MAXBSIZE / MIN_PAGE_SIZE], *pg;
off_t soff = 0; /* XXX: gcc */
voff_t off;
@@ -258,39 +256,11 @@ check_dirty(struct lfs *fs, struct vnode
KASSERT(mutex_owned(vp->v_interlock));
ASSERT_MAYBE_SEGLOCK(fs);
top:
- by_list = (vp->v_uobj.uo_npages <=
- ((endoffset - startoffset) >> PAGE_SHIFT) *
- UVM_PAGE_TREE_PENALTY);
any_dirty = 0;
- if (by_list) {
- curpg = TAILQ_FIRST(&vp->v_uobj.memq);
- } else {
- soff = startoffset;
- }
- while (by_list || soff < MIN(blkeof, endoffset)) {
- if (by_list) {
- /*
- * Find the first page in a block. Skip
- * blocks outside our area of interest or beyond
- * the end of file.
- */
- KASSERT(curpg == NULL
- || (curpg->flags & PG_MARKER) == 0);
- if (pages_per_block > 1) {
- while (curpg &&
- ((curpg->offset & lfs_sb_getbmask(fs)) ||
- curpg->offset >= vp->v_size ||
- curpg->offset >= endoffset)) {
- curpg = TAILQ_NEXT(curpg, listq.queue);
- KASSERT(curpg == NULL ||
- (curpg->flags & PG_MARKER) == 0);
- }
- }
- if (curpg == NULL)
- break;
- soff = curpg->offset;
- }
+ soff = startoffset;
+ KASSERT((soff & (lfs_sb_getbsize(fs) - 1)) == 0);
+ while (soff < MIN(blkeof, endoffset)) {
/*
* Mark all pages in extended range busy; find out if any
@@ -299,15 +269,11 @@ check_dirty(struct lfs *fs, struct vnode
nonexistent = dirty = 0;
for (i = 0; i == 0 || i < pages_per_block; i++) {
KASSERT(mutex_owned(vp->v_interlock));
- if (by_list && pages_per_block <= 1) {
- pgs[i] = pg = curpg;
- } else {
- off = soff + (i << PAGE_SHIFT);
- pgs[i] = pg = uvm_pagelookup(&vp->v_uobj, off);
- if (pg == NULL) {
- ++nonexistent;
- continue;
- }
+ off = soff + (i << PAGE_SHIFT);
+ pgs[i] = pg = uvm_pagelookup(&vp->v_uobj, off);
+ if (pg == NULL) {
+ ++nonexistent;
+ continue;
}
KASSERT(pg != NULL);
@@ -344,12 +310,8 @@ check_dirty(struct lfs *fs, struct vnode
(pg->flags & PG_CLEAN) == 0);
dirty += tdirty;
}
- if (pages_per_block > 0 && nonexistent >= pages_per_block) {
- if (by_list) {
- curpg = TAILQ_NEXT(curpg, listq.queue);
- } else {
- soff += lfs_sb_getbsize(fs);
- }
+ if (nonexistent >= pages_per_block) {
+ soff += MAX(PAGE_SIZE, lfs_sb_getbsize(fs));
continue;
}
@@ -390,11 +352,7 @@ check_dirty(struct lfs *fs, struct vnode
if (checkfirst && any_dirty)
break;
- if (by_list) {
- curpg = TAILQ_NEXT(curpg, listq.queue);
- } else {
- soff += MAX(PAGE_SIZE, lfs_sb_getbsize(fs));
- }
+ soff += MAX(PAGE_SIZE, lfs_sb_getbsize(fs));
}
KASSERT(mutex_owned(vp->v_interlock));
@@ -492,8 +450,7 @@ retry:
* If there are no pages, don't do anything.
*/
if (vp->v_uobj.uo_npages == 0) {
- if (TAILQ_EMPTY(&vp->v_uobj.memq) &&
- (vp->v_iflag & VI_ONWORKLST) &&
+ if ((vp->v_iflag & VI_ONWORKLST) &&
LIST_FIRST(&vp->v_dirtyblkhd) == NULL) {
vp->v_iflag &= ~VI_WRMAPDIRTY;
vn_syncer_remove_from_worklist(vp);
Index: src/sys/uvm/files.uvm
diff -u src/sys/uvm/files.uvm:1.30 src/sys/uvm/files.uvm:1.31
--- src/sys/uvm/files.uvm:1.30 Wed Nov 20 19:37:54 2019
+++ src/sys/uvm/files.uvm Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-# $NetBSD: files.uvm,v 1.30 2019/11/20 19:37:54 pgoyette Exp $
+# $NetBSD: files.uvm,v 1.31 2019/12/15 21:11:35 ad Exp $
#
# UVM options
@@ -37,6 +37,7 @@ file uvm/uvm_mmap.c uvm
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_pager.c uvm
file uvm/uvm_pdaemon.c uvm
file uvm/uvm_pdpolicy_clock.c !pdpolicy_clockpro
Index: src/sys/uvm/uvm_aobj.c
diff -u src/sys/uvm/uvm_aobj.c:1.131 src/sys/uvm/uvm_aobj.c:1.132
--- src/sys/uvm/uvm_aobj.c:1.131 Fri Dec 13 20:10:22 2019
+++ src/sys/uvm/uvm_aobj.c Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_aobj.c,v 1.131 2019/12/13 20:10:22 ad Exp $ */
+/* $NetBSD: uvm_aobj.c,v 1.132 2019/12/15 21:11:35 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.131 2019/12/13 20:10:22 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v 1.132 2019/12/15 21:11:35 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_uvmhist.h"
@@ -52,6 +52,7 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v
#include <sys/atomic.h>
#include <uvm/uvm.h>
+#include <uvm/uvm_page_array.h>
/*
* An anonymous UVM object (aobj) manages anonymous-memory. In addition to
@@ -137,7 +138,7 @@ static struct pool uao_swhash_elt_pool _
*/
struct uvm_aobj {
- struct uvm_object u_obj; /* has: lock, pgops, memq, #pages, #refs */
+ struct uvm_object u_obj; /* has: lock, pgops, #pages, #refs */
pgoff_t u_pages; /* number of pages in entire object */
int u_flags; /* the flags (see uvm_aobj.h) */
int *u_swslots; /* array of offset->swapslot mappings */
@@ -575,6 +576,7 @@ void
uao_detach(struct uvm_object *uobj)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
+ struct uvm_page_array a;
struct vm_page *pg;
UVMHIST_FUNC("uao_detach"); UVMHIST_CALLED(maphist);
@@ -612,19 +614,24 @@ uao_detach(struct uvm_object *uobj)
* involved in is complete), release any swap resources and free
* the page itself.
*/
+ uvm_page_array_init(&a);
mutex_enter(uobj->vmobjlock);
- while ((pg = TAILQ_FIRST(&uobj->memq)) != NULL) {
+ while ((pg = uvm_page_array_fill_and_peek(&a, uobj, 0, 0, 0))
+ != NULL) {
+ uvm_page_array_advance(&a);
pmap_page_protect(pg, VM_PROT_NONE);
if (pg->flags & PG_BUSY) {
pg->flags |= PG_WANTED;
UVM_UNLOCK_AND_WAIT(pg, uobj->vmobjlock, false,
"uao_det", 0);
+ uvm_page_array_clear(&a);
mutex_enter(uobj->vmobjlock);
continue;
}
uao_dropswap(&aobj->u_obj, pg->offset >> PAGE_SHIFT);
uvm_pagefree(pg);
}
+ uvm_page_array_fini(&a);
/*
* Finally, free the anonymous UVM object itself.
@@ -646,45 +653,25 @@ uao_detach(struct uvm_object *uobj)
* or block.
* => if PGO_ALLPAGE is set, then all pages in the object are valid targets
* for flushing.
- * => NOTE: we rely on the fact that the object's memq is a TAILQ and
- * that new pages are inserted on the tail end of the list. thus,
- * we can make a complete pass through the object in one go by starting
- * at the head and working towards the tail (new pages are put in
- * front of us).
* => we return 0 unless we encountered some sort of I/O error
* XXXJRT currently never happens, as we never directly initiate
* XXXJRT I/O
- *
- * note on page traversal:
- * we can traverse the pages in an object either by going down the
- * linked list in "uobj->memq", or we can go over the address range
- * by page doing hash table lookups for each address. depending
- * on how many pages are in the object it may be cheaper to do one
- * or the other. we set "by_list" to true if we are using memq.
- * if the cost of a hash lookup was equal to the cost of the list
- * traversal we could compare the number of pages in the start->stop
- * range to the total number of pages in the object. however, it
- * seems that a hash table lookup is more expensive than the linked
- * list traversal, so we multiply the number of pages in the
- * start->stop range by a penalty which we define below.
*/
static int
uao_put(struct uvm_object *uobj, voff_t start, voff_t stop, int flags)
{
struct uvm_aobj *aobj = (struct uvm_aobj *)uobj;
- struct vm_page *pg, *nextpg, curmp, endmp;
- bool by_list;
+ struct uvm_page_array a;
+ struct vm_page *pg;
voff_t curoff;
UVMHIST_FUNC("uao_put"); UVMHIST_CALLED(maphist);
KASSERT(mutex_owned(uobj->vmobjlock));
- curoff = 0;
if (flags & PGO_ALLPAGES) {
start = 0;
stop = aobj->u_pages << PAGE_SHIFT;
- by_list = true; /* always go by the list */
} else {
start = trunc_page(start);
if (stop == 0) {
@@ -699,12 +686,10 @@ uao_put(struct uvm_object *uobj, voff_t
(uintmax_t)(aobj->u_pages << PAGE_SHIFT));
stop = aobj->u_pages << PAGE_SHIFT;
}
- by_list = (uobj->uo_npages <=
- ((stop - start) >> PAGE_SHIFT) * UVM_PAGE_TREE_PENALTY);
}
UVMHIST_LOG(maphist,
- " flush start=0x%jx, stop=0x%jx, by_list=%jd, flags=0x%jx",
- start, stop, by_list, flags);
+ " flush start=0x%jx, stop=0x%jx, flags=0x%jx",
+ start, stop, flags, 0);
/*
* Don't need to do any work here if we're not freeing
@@ -716,47 +701,13 @@ uao_put(struct uvm_object *uobj, voff_t
return 0;
}
- /*
- * Initialize the marker pages. See the comment in
- * genfs_putpages() also.
- */
-
- curmp.flags = PG_MARKER;
- endmp.flags = PG_MARKER;
-
- /*
- * now do it. note: we must update nextpg in the body of loop or we
- * will get stuck. we need to use nextpg if we'll traverse the list
- * because we may free "pg" before doing the next loop.
- */
-
- if (by_list) {
- TAILQ_INSERT_TAIL(&uobj->memq, &endmp, listq.queue);
- nextpg = TAILQ_FIRST(&uobj->memq);
- } else {
- curoff = start;
- nextpg = NULL; /* Quell compiler warning */
- }
-
/* locked: uobj */
- for (;;) {
- if (by_list) {
- pg = nextpg;
- if (pg == &endmp)
- break;
- nextpg = TAILQ_NEXT(pg, listq.queue);
- if (pg->flags & PG_MARKER)
- continue;
- if (pg->offset < start || pg->offset >= stop)
- continue;
- } else {
- if (curoff < stop) {
- pg = uvm_pagelookup(uobj, curoff);
- curoff += PAGE_SIZE;
- } else
- break;
- if (pg == NULL)
- continue;
+ uvm_page_array_init(&a);
+ curoff = start;
+ while ((pg = uvm_page_array_fill_and_peek(&a, uobj, curoff, 0, 0)) !=
+ NULL) {
+ if (pg->offset >= stop) {
+ break;
}
/*
@@ -764,21 +715,15 @@ uao_put(struct uvm_object *uobj, voff_t
*/
if (pg->flags & PG_BUSY) {
- if (by_list) {
- TAILQ_INSERT_BEFORE(pg, &curmp, listq.queue);
- }
pg->flags |= PG_WANTED;
UVM_UNLOCK_AND_WAIT(pg, uobj->vmobjlock, 0,
"uao_put", 0);
+ uvm_page_array_clear(&a);
mutex_enter(uobj->vmobjlock);
- if (by_list) {
- nextpg = TAILQ_NEXT(&curmp, listq.queue);
- TAILQ_REMOVE(&uobj->memq, &curmp,
- listq.queue);
- } else
- curoff -= PAGE_SIZE;
continue;
}
+ uvm_page_array_advance(&a);
+ curoff = pg->offset + PAGE_SIZE;
switch (flags & (PGO_CLEANIT|PGO_FREE|PGO_DEACTIVATE)) {
@@ -825,10 +770,8 @@ uao_put(struct uvm_object *uobj, voff_t
panic("%s: impossible", __func__);
}
}
- if (by_list) {
- TAILQ_REMOVE(&uobj->memq, &endmp, listq.queue);
- }
mutex_exit(uobj->vmobjlock);
+ uvm_page_array_fini(&a);
return 0;
}
Index: src/sys/uvm/uvm_loan.c
diff -u src/sys/uvm/uvm_loan.c:1.90 src/sys/uvm/uvm_loan.c:1.91
--- src/sys/uvm/uvm_loan.c:1.90 Sat Dec 14 15:08:45 2019
+++ src/sys/uvm/uvm_loan.c Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_loan.c,v 1.90 2019/12/14 15:08:45 ad Exp $ */
+/* $NetBSD: uvm_loan.c,v 1.91 2019/12/15 21:11:35 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.90 2019/12/14 15:08:45 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v 1.91 2019/12/15 21:11:35 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -844,8 +844,8 @@ again:
* first, get ahold of our single zero page.
*/
- if (__predict_false((pg =
- TAILQ_FIRST(&uvm_loanzero_object.memq)) == NULL)) {
+ pg = uvm_pagelookup(&uvm_loanzero_object, 0);
+ if (__predict_false(pg == NULL)) {
while ((pg = uvm_pagealloc(&uvm_loanzero_object, 0, NULL,
UVM_PGA_ZERO)) == NULL) {
mutex_exit(uvm_loanzero_object.vmobjlock);
@@ -1060,13 +1060,12 @@ ulz_put(struct uvm_object *uobj, voff_t
* just reactivate or dequeue it.
*/
- pg = TAILQ_FIRST(&uobj->memq);
+ pg = uvm_pagelookup(uobj, 0);
KASSERT(pg != NULL);
- KASSERT(TAILQ_NEXT(pg, listq.queue) == NULL);
- if (pg->uanon)
+ if (pg->uanon) {
uvm_pageactivate(pg);
- else {
+ } else {
uvm_pagedequeue(pg);
}
Index: src/sys/uvm/uvm_object.c
diff -u src/sys/uvm/uvm_object.c:1.17 src/sys/uvm/uvm_object.c:1.18
--- src/sys/uvm/uvm_object.c:1.17 Sat Dec 14 17:28:58 2019
+++ src/sys/uvm/uvm_object.c Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_object.c,v 1.17 2019/12/14 17:28:58 ad Exp $ */
+/* $NetBSD: uvm_object.c,v 1.18 2019/12/15 21:11:35 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.17 2019/12/14 17:28:58 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_object.c,v 1.18 2019/12/15 21:11:35 ad Exp $");
#ifdef _KERNEL_OPT
#include "opt_ddb.h"
@@ -49,6 +49,7 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_object.c
#include <uvm/uvm.h>
#include <uvm/uvm_ddb.h>
+#include <uvm/uvm_page_array.h>
/* Page count to fetch per single step. */
#define FETCH_PAGECOUNT 16
@@ -72,7 +73,6 @@ uvm_obj_init(struct uvm_object *uo, cons
uo->vmobjlock = NULL;
}
uo->pgops = ops;
- TAILQ_INIT(&uo->memq);
LIST_INIT(&uo->uo_ubc);
uo->uo_npages = 0;
uo->uo_refs = refs;
@@ -237,8 +237,10 @@ void
uvm_object_printit(struct uvm_object *uobj, bool full,
void (*pr)(const char *, ...))
{
+ struct uvm_page_array a;
struct vm_page *pg;
int cnt = 0;
+ voff_t off;
(*pr)("OBJECT %p: locked=%d, pgops=%p, npages=%d, ",
uobj, mutex_owned(uobj->vmobjlock), uobj->pgops, uobj->uo_npages);
@@ -251,16 +253,22 @@ uvm_object_printit(struct uvm_object *uo
return;
}
(*pr)(" PAGES <pg,offset>:\n ");
- TAILQ_FOREACH(pg, &uobj->memq, listq.queue) {
+ uvm_page_array_init(&a);
+ off = 0;
+ while ((pg = uvm_page_array_fill_and_peek(&a, uobj, off, 0, 0))
+ != NULL) {
cnt++;
(*pr)("<%p,0x%llx> ", pg, (long long)pg->offset);
if ((cnt % 3) == 0) {
(*pr)("\n ");
}
+ off = pg->offset + PAGE_SIZE;
+ uvm_page_array_advance(&a);
}
if ((cnt % 3) != 0) {
(*pr)("\n");
}
+ uvm_page_array_fini(&a);
}
#endif /* DDB || DEBUGPRINT */
Index: src/sys/uvm/uvm_object.h
diff -u src/sys/uvm/uvm_object.h:1.34 src/sys/uvm/uvm_object.h:1.35
--- src/sys/uvm/uvm_object.h:1.34 Sat Dec 14 17:28:58 2019
+++ src/sys/uvm/uvm_object.h Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_object.h,v 1.34 2019/12/14 17:28:58 ad Exp $ */
+/* $NetBSD: uvm_object.h,v 1.35 2019/12/15 21:11:35 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -56,7 +56,6 @@
struct uvm_object {
kmutex_t * vmobjlock; /* lock on object */
const struct uvm_pagerops *pgops; /* pager ops */
- struct pglist memq; /* pages in this object */
int uo_npages; /* # of pages in uo_pages */
unsigned uo_refs; /* reference count */
struct radix_tree uo_pages; /* tree of pages */
Index: src/sys/uvm/uvm_page.c
diff -u src/sys/uvm/uvm_page.c:1.202 src/sys/uvm/uvm_page.c:1.203
--- src/sys/uvm/uvm_page.c:1.202 Sat Dec 14 17:28:58 2019
+++ src/sys/uvm/uvm_page.c Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_page.c,v 1.202 2019/12/14 17:28:58 ad Exp $ */
+/* $NetBSD: uvm_page.c,v 1.203 2019/12/15 21:11:35 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.202 2019/12/14 17:28:58 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.203 2019/12/15 21:11:35 ad Exp $");
#include "opt_ddb.h"
#include "opt_uvm.h"
@@ -167,15 +167,12 @@ static void uvm_pageremove(struct uvm_ob
*/
static inline void
-uvm_pageinsert_list(struct uvm_object *uobj, struct vm_page *pg,
- struct vm_page *where)
+uvm_pageinsert_object(struct uvm_object *uobj, struct vm_page *pg)
{
KASSERT(uobj == pg->uobject);
KASSERT(mutex_owned(uobj->vmobjlock));
KASSERT((pg->flags & PG_TABLED) == 0);
- KASSERT(where == NULL || (where->flags & PG_TABLED));
- KASSERT(where == NULL || (where->uobject == uobj));
if (UVM_OBJ_IS_VNODE(uobj)) {
if (uobj->uo_npages == 0) {
@@ -191,11 +188,6 @@ uvm_pageinsert_list(struct uvm_object *u
} else if (UVM_OBJ_IS_AOBJ(uobj)) {
atomic_inc_uint(&uvmexp.anonpages);
}
-
- if (where)
- TAILQ_INSERT_AFTER(&uobj->memq, where, pg, listq.queue);
- else
- TAILQ_INSERT_TAIL(&uobj->memq, pg, listq.queue);
pg->flags |= PG_TABLED;
uobj->uo_npages++;
}
@@ -225,7 +217,7 @@ uvm_pageinsert(struct uvm_object *uobj,
KASSERT(error == ENOMEM);
return error;
}
- uvm_pageinsert_list(uobj, pg, NULL);
+ uvm_pageinsert_object(uobj, pg);
return error;
}
@@ -236,7 +228,7 @@ uvm_pageinsert(struct uvm_object *uobj,
*/
static inline void
-uvm_pageremove_list(struct uvm_object *uobj, struct vm_page *pg)
+uvm_pageremove_object(struct uvm_object *uobj, struct vm_page *pg)
{
KASSERT(uobj == pg->uobject);
@@ -260,7 +252,6 @@ uvm_pageremove_list(struct uvm_object *u
/* object should be locked */
uobj->uo_npages--;
- TAILQ_REMOVE(&uobj->memq, pg, listq.queue);
pg->flags &= ~PG_TABLED;
pg->uobject = NULL;
}
@@ -280,7 +271,7 @@ uvm_pageremove(struct uvm_object *uobj,
KDASSERT(uobj != NULL);
KASSERT(uobj == pg->uobject);
- uvm_pageremove_list(uobj, pg);
+ uvm_pageremove_object(uobj, pg);
uvm_pageremove_tree(uobj, pg);
}
@@ -1105,8 +1096,8 @@ uvm_pagereplace(struct vm_page *oldpg, s
uvm_pageremove_tree(uobj, oldpg);
uvm_pageinsert_tree(uobj, newpg);
- uvm_pageinsert_list(uobj, newpg, oldpg);
- uvm_pageremove_list(uobj, oldpg);
+ uvm_pageinsert_object(uobj, newpg);
+ uvm_pageremove_object(uobj, oldpg);
}
/*
@@ -1542,7 +1533,7 @@ uvm_pagelookup(struct uvm_object *obj, v
{
struct vm_page *pg;
- KASSERT(mutex_owned(obj->vmobjlock));
+ /* No - used from DDB. KASSERT(mutex_owned(obj->vmobjlock)); */
pg = radix_tree_lookup_node(&obj->uo_pages, off >> PAGE_SHIFT);
@@ -1841,11 +1832,7 @@ uvm_page_printit(struct vm_page *pg, boo
uobj = pg->uobject;
if (uobj) {
(*pr)(" checking object list\n");
- TAILQ_FOREACH(tpg, &uobj->memq, listq.queue) {
- if (tpg == pg) {
- break;
- }
- }
+ tpg = uvm_pagelookup(uobj, pg->offset);
if (tpg)
(*pr)(" page found on object list\n");
else
Index: src/sys/uvm/uvm_page.h
diff -u src/sys/uvm/uvm_page.h:1.86 src/sys/uvm/uvm_page.h:1.87
--- src/sys/uvm/uvm_page.h:1.86 Sat Dec 14 17:31:53 2019
+++ src/sys/uvm/uvm_page.h Sun Dec 15 21:11:35 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_page.h,v 1.86 2019/12/14 17:31:53 ad Exp $ */
+/* $NetBSD: uvm_page.h,v 1.87 2019/12/15 21:11:35 ad Exp $ */
/*
* Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -125,7 +125,6 @@
* => PG_FREE is set in flags
* o owned by a uvm_object
* => pageq.queue is entry on wired page queue, if any
- * => listq.queue is entry on list of pages in object
* => uanon is NULL or the vm_anon to which it has been O->A loaned
* => uobject is owner
* o owned by a vm_anon
@@ -156,7 +155,6 @@ struct vm_page {
} pageq;
union {
- TAILQ_ENTRY(vm_page) queue; /* o: pages in same object */
LIST_ENTRY(vm_page) list; /* f: CPU free page queue */
} listq;
Added files:
Index: src/sys/uvm/uvm_page_array.c
diff -u /dev/null src/sys/uvm/uvm_page_array.c:1.2
--- /dev/null Sun Dec 15 21:11:35 2019
+++ src/sys/uvm/uvm_page_array.c Sun Dec 15 21:11:35 2019
@@ -0,0 +1,217 @@
+/* $NetBSD: uvm_page_array.c,v 1.2 2019/12/15 21:11:35 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_array.c,v 1.2 2019/12/15 21:11:35 ad Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <uvm/uvm_extern.h>
+#include <uvm/uvm_object.h>
+#include <uvm/uvm_page.h>
+#include <uvm/uvm_page_array.h>
+
+/*
+ * uvm_page_array_init: initialize the array.
+ */
+
+void
+uvm_page_array_init(struct uvm_page_array *ar)
+{
+
+ ar->ar_idx = ar->ar_npages = 0;
+}
+
+/*
+ * uvm_page_array_fini: clean up the array.
+ */
+
+void
+uvm_page_array_fini(struct uvm_page_array *ar)
+{
+
+ /*
+ * currently nothing to do.
+ */
+#if defined(DIAGNOSTIC)
+ /*
+ * poison to trigger assertion in uvm_page_array_peek to
+ * detect usage errors.
+ */
+ ar->ar_npages = 1;
+ ar->ar_idx = 1000;
+#endif /* defined(DIAGNOSTIC) */
+}
+
+/*
+ * uvm_page_array_clear: forget the cached pages and initialize the array.
+ */
+
+void
+uvm_page_array_clear(struct uvm_page_array *ar)
+{
+
+ KASSERT(ar->ar_idx <= ar->ar_npages);
+ uvm_page_array_init(ar);
+}
+
+/*
+ * uvm_page_array_peek: return the next cached page.
+ */
+
+struct vm_page *
+uvm_page_array_peek(struct uvm_page_array *ar)
+{
+
+ KASSERT(ar->ar_idx <= ar->ar_npages);
+ if (ar->ar_idx == ar->ar_npages) {
+ return NULL;
+ }
+ return ar->ar_pages[ar->ar_idx];
+}
+
+/*
+ * uvm_page_array_advance: advance the array to the next cached page
+ */
+
+void
+uvm_page_array_advance(struct uvm_page_array *ar)
+{
+
+ KASSERT(ar->ar_idx <= ar->ar_npages);
+ ar->ar_idx++;
+ KASSERT(ar->ar_idx <= ar->ar_npages);
+}
+
+/*
+ * uvm_page_array_fill: lookup pages and keep them cached.
+ *
+ * return 0 on success. in that case, cache the result in the array
+ * so that they will be picked by later uvm_page_array_peek.
+ *
+ * nwant is a number of pages to fetch. a caller should consider it a hint.
+ * nwant == 0 means a caller have no specific idea.
+ *
+ * return ENOENT if no pages are found.
+ *
+ * called with object lock held.
+ */
+
+int
+uvm_page_array_fill(struct uvm_page_array *ar, struct uvm_object *uobj,
+ voff_t off, unsigned int nwant, unsigned int flags)
+{
+ unsigned int npages;
+#if defined(DEBUG)
+ unsigned int i;
+#endif /* defined(DEBUG) */
+ unsigned int maxpages = __arraycount(ar->ar_pages);
+ const bool dense = (flags & UVM_PAGE_ARRAY_FILL_DENSE) != 0;
+ const bool backward = (flags & UVM_PAGE_ARRAY_FILL_BACKWARD) != 0;
+
+ if (nwant != 0 && nwant < maxpages) {
+ maxpages = nwant;
+ }
+#if 0 /* called from DDB for "show obj/f" without lock */
+ 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;
+
+ if ((flags & UVM_PAGE_ARRAY_FILL_WRITEBACK) != 0) {
+ tagmask |= UVM_PAGE_WRITEBACK_TAG;
+ }
+ npages =
+ (backward ? radix_tree_gang_lookup_tagged_node_reverse :
+ radix_tree_gang_lookup_tagged_node)(
+ &uobj->uo_pages, off >> PAGE_SHIFT, (void **)ar->ar_pages,
+ maxpages, dense, tagmask);
+ } else
+#endif
+ {
+ npages =
+ (backward ? radix_tree_gang_lookup_node_reverse :
+ radix_tree_gang_lookup_node)(
+ &uobj->uo_pages, off >> PAGE_SHIFT, (void **)ar->ar_pages,
+ maxpages, dense);
+ }
+ if (npages == 0) {
+ uvm_page_array_clear(ar);
+ return ENOENT;
+ }
+ KASSERT(npages <= maxpages);
+ ar->ar_npages = npages;
+ ar->ar_idx = 0;
+#if defined(DEBUG)
+ for (i = 0; i < ar->ar_npages; i++) {
+ struct vm_page * const pg = ar->ar_pages[i];
+
+ KDASSERT(pg != NULL);
+ KDASSERT(pg->uobject == uobj);
+ if (backward) {
+ KDASSERT(pg->offset <= off);
+ KDASSERT(i == 0 ||
+ pg->offset < ar->ar_pages[i - 1]->offset);
+ } else {
+ KDASSERT(pg->offset >= off);
+ KDASSERT(i == 0 ||
+ pg->offset > ar->ar_pages[i - 1]->offset);
+ }
+ }
+#endif /* defined(DEBUG) */
+ return 0;
+}
+
+/*
+ * uvm_page_array_fill_and_peek:
+ * same as uvm_page_array_peek except that, if the array is empty, try to fill
+ * it first.
+ */
+
+struct vm_page *
+uvm_page_array_fill_and_peek(struct uvm_page_array *a, struct uvm_object *uobj,
+ voff_t off, unsigned int nwant, unsigned int flags)
+{
+ struct vm_page *pg;
+ int error;
+
+ pg = uvm_page_array_peek(a);
+ if (pg != NULL) {
+ return pg;
+ }
+ error = uvm_page_array_fill(a, uobj, off, nwant, flags);
+ if (error != 0) {
+ return NULL;
+ }
+ pg = uvm_page_array_peek(a);
+ KASSERT(pg != NULL);
+ return pg;
+}
Index: src/sys/uvm/uvm_page_array.h
diff -u /dev/null src/sys/uvm/uvm_page_array.h:1.2
--- /dev/null Sun Dec 15 21:11:35 2019
+++ src/sys/uvm/uvm_page_array.h Sun Dec 15 21:11:35 2019
@@ -0,0 +1,79 @@
+/* $NetBSD: uvm_page_array.h,v 1.2 2019/12/15 21:11:35 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.
+ */
+
+#if !defined(_UVM_UVM_PAGE_ARRAY_H_)
+#define _UVM_UVM_PAGE_ARRAY_H_
+
+/*
+ * uvm_page_array: an array of pages.
+ *
+ * these structure and functions simply manipulate struct vm_page pointers.
+ * it's caller's responsibity to acquire and keep the object lock so that
+ * the result is valid.
+ *
+ * typical usage:
+ *
+ * struct uvm_page_array a;
+ *
+ * uvm_page_array_init(&a);
+ * while ((pg = uvm_page_array_fill_and_peek(&a, uobj, off, ....))
+ * != NULL) {
+ * off = pg->offset + PAGE_SIZE;
+ * do_something(pg);
+ * uvm_page_array_advance(&a);
+ * }
+ * uvm_page_array_fini(&a);
+ */
+
+struct vm_page;
+
+struct uvm_page_array {
+ struct vm_page *ar_pages[16]; /* XXX tune */
+ unsigned int ar_npages; /* valid elements in ar_pages */
+ unsigned int ar_idx; /* index in ar_pages */
+};
+
+void uvm_page_array_init(struct uvm_page_array *);
+void uvm_page_array_fini(struct uvm_page_array *);
+void uvm_page_array_clear(struct uvm_page_array *);
+struct vm_page *uvm_page_array_peek(struct uvm_page_array *);
+void uvm_page_array_advance(struct uvm_page_array *);
+int uvm_page_array_fill(struct uvm_page_array *, struct uvm_object *,
+ voff_t, unsigned int, unsigned int);
+struct vm_page *uvm_page_array_fill_and_peek(struct uvm_page_array *,
+ struct uvm_object *, voff_t, unsigned int, unsigned int);
+
+/*
+ * flags for uvm_page_array_fill and uvm_page_array_fill_and_peek
+ */
+#define UVM_PAGE_ARRAY_FILL_DIRTY 1 /* dirty pages */
+#define UVM_PAGE_ARRAY_FILL_WRITEBACK 2 /* dirty or written-back */
+#define UVM_PAGE_ARRAY_FILL_DENSE 4 /* stop on a hole */
+#define UVM_PAGE_ARRAY_FILL_BACKWARD 8 /* descend order */
+
+#endif /* defined(_UVM_UVM_ARRAY_H_) */