Module Name:    src
Committed By:   ad
Date:           Fri Dec 13 20:10:22 UTC 2019

Modified Files:
        src/external/cddl/osnet/dist/uts/common/fs/zfs: zfs_vnops.c
        src/sys/miscfs/genfs: genfs_io.c
        src/sys/nfs: nfs_bio.c
        src/sys/rump/librump/rumpkern: vm.c
        src/sys/rump/librump/rumpvfs: vm_vfs.c
        src/sys/ufs/lfs: lfs_pages.c lfs_vfsops.c ulfs_inode.c
        src/sys/ufs/ufs: ufs_inode.c
        src/sys/uvm: uvm.h uvm_amap.c uvm_anon.c uvm_aobj.c uvm_bio.c
            uvm_fault.c uvm_init.c uvm_km.c uvm_loan.c uvm_map.c uvm_object.c
            uvm_page.c uvm_page.h uvm_pager.c uvm_pdaemon.c uvm_pdpolicy.h
            uvm_pdpolicy_clock.c uvm_pdpolicy_clockpro.c uvm_pglist.c
            uvm_physseg.c

Log Message:
Break the global uvm_pageqlock into a per-page identity lock and a private
lock for use of the pagedaemon policy code.  Discussed on tech-kern.

PR kern/54209: NetBSD 8 large memory performance extremely low
PR kern/54210: NetBSD-8 processes presumably not exiting
PR kern/54727: writing a large file causes unreasonable system behaviour


To generate a diff of this commit:
cvs rdiff -u -r1.53 -r1.54 \
    src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c
cvs rdiff -u -r1.76 -r1.77 src/sys/miscfs/genfs/genfs_io.c
cvs rdiff -u -r1.191 -r1.192 src/sys/nfs/nfs_bio.c
cvs rdiff -u -r1.173 -r1.174 src/sys/rump/librump/rumpkern/vm.c
cvs rdiff -u -r1.34 -r1.35 src/sys/rump/librump/rumpvfs/vm_vfs.c
cvs rdiff -u -r1.15 -r1.16 src/sys/ufs/lfs/lfs_pages.c
cvs rdiff -u -r1.365 -r1.366 src/sys/ufs/lfs/lfs_vfsops.c
cvs rdiff -u -r1.21 -r1.22 src/sys/ufs/lfs/ulfs_inode.c
cvs rdiff -u -r1.105 -r1.106 src/sys/ufs/ufs/ufs_inode.c
cvs rdiff -u -r1.69 -r1.70 src/sys/uvm/uvm.h
cvs rdiff -u -r1.110 -r1.111 src/sys/uvm/uvm_amap.c
cvs rdiff -u -r1.68 -r1.69 src/sys/uvm/uvm_anon.c
cvs rdiff -u -r1.130 -r1.131 src/sys/uvm/uvm_aobj.c
cvs rdiff -u -r1.100 -r1.101 src/sys/uvm/uvm_bio.c
cvs rdiff -u -r1.211 -r1.212 src/sys/uvm/uvm_fault.c
cvs rdiff -u -r1.50 -r1.51 src/sys/uvm/uvm_init.c
cvs rdiff -u -r1.150 -r1.151 src/sys/uvm/uvm_km.c
cvs rdiff -u -r1.88 -r1.89 src/sys/uvm/uvm_loan.c
cvs rdiff -u -r1.366 -r1.367 src/sys/uvm/uvm_map.c
cvs rdiff -u -r1.15 -r1.16 src/sys/uvm/uvm_object.c
cvs rdiff -u -r1.200 -r1.201 src/sys/uvm/uvm_page.c
cvs rdiff -u -r1.84 -r1.85 src/sys/uvm/uvm_page.h
cvs rdiff -u -r1.113 -r1.114 src/sys/uvm/uvm_pager.c
cvs rdiff -u -r1.112 -r1.113 src/sys/uvm/uvm_pdaemon.c
cvs rdiff -u -r1.3 -r1.4 src/sys/uvm/uvm_pdpolicy.h
cvs rdiff -u -r1.17 -r1.18 src/sys/uvm/uvm_pdpolicy_clock.c \
    src/sys/uvm/uvm_pdpolicy_clockpro.c
cvs rdiff -u -r1.72 -r1.73 src/sys/uvm/uvm_pglist.c
cvs rdiff -u -r1.10 -r1.11 src/sys/uvm/uvm_physseg.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.53 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.53	Fri Oct  4 23:06:19 2019
+++ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_vnops.c	Fri Dec 13 20:10:21 2019
@@ -6081,9 +6081,7 @@ zfs_putapage(vnode_t *vp, page_t **pp, i
 
 out_unbusy:
 	mutex_enter(mtx);
-	mutex_enter(&uvm_pageqlock);
 	uvm_page_unbusy(pp, count);
-	mutex_exit(&uvm_pageqlock);
 	mutex_exit(mtx);
 
 out:

Index: src/sys/miscfs/genfs/genfs_io.c
diff -u src/sys/miscfs/genfs/genfs_io.c:1.76 src/sys/miscfs/genfs/genfs_io.c:1.77
--- src/sys/miscfs/genfs/genfs_io.c:1.76	Sun Oct  6 05:48:00 2019
+++ src/sys/miscfs/genfs/genfs_io.c	Fri Dec 13 20:10:21 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: genfs_io.c,v 1.76 2019/10/06 05:48:00 mlelstv Exp $	*/
+/*	$NetBSD: genfs_io.c,v 1.77 2019/12/13 20:10:21 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.76 2019/10/06 05:48:00 mlelstv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: genfs_io.c,v 1.77 2019/12/13 20:10:21 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -79,9 +79,7 @@ genfs_rel_pages(struct vm_page **pgs, un
 			pg->flags |= PG_RELEASED;
 		}
 	}
-	mutex_enter(&uvm_pageqlock);
 	uvm_page_unbusy(pgs, npages);
-	mutex_exit(&uvm_pageqlock);
 }
 
 static void
@@ -466,7 +464,6 @@ startover:
 out:
 	UVMHIST_LOG(ubchist, "succeeding, npages %jd", npages,0,0,0);
 	error = 0;
-	mutex_enter(&uvm_pageqlock);
 	for (i = 0; i < npages; i++) {
 		struct vm_page *pg = pgs[i];
 		if (pg == NULL) {
@@ -498,7 +495,6 @@ out:
 			UVM_PAGE_OWN(pg, NULL);
 		}
 	}
-	mutex_exit(&uvm_pageqlock);
 	if (memwrite) {
 		genfs_markdirty(vp);
 	}
@@ -1201,9 +1197,6 @@ retry:
 		 * apply FREE or DEACTIVATE options if requested.
 		 */
 
-		if (flags & (PGO_DEACTIVATE|PGO_FREE)) {
-			mutex_enter(&uvm_pageqlock);
-		}
 		for (i = 0; i < npages; i++) {
 			tpg = pgs[i];
 			KASSERT(tpg->uobject == uobj);
@@ -1236,9 +1229,6 @@ retry:
 				}
 			}
 		}
-		if (flags & (PGO_DEACTIVATE|PGO_FREE)) {
-			mutex_exit(&uvm_pageqlock);
-		}
 		if (needs_clean) {
 			modified = true;
 
@@ -1646,7 +1636,6 @@ genfs_compat_getpages(void *v)
 	}
 	uvm_pagermapout(kva, npages);
 	mutex_enter(uobj->vmobjlock);
-	mutex_enter(&uvm_pageqlock);
 	for (i = 0; i < npages; i++) {
 		pg = pgs[i];
 		if (error && (pg->flags & PG_FAKE) != 0) {
@@ -1659,7 +1648,6 @@ genfs_compat_getpages(void *v)
 	if (error) {
 		uvm_page_unbusy(pgs, npages);
 	}
-	mutex_exit(&uvm_pageqlock);
 	if (error == 0 && memwrite) {
 		genfs_markdirty(vp);
 	}

Index: src/sys/nfs/nfs_bio.c
diff -u src/sys/nfs/nfs_bio.c:1.191 src/sys/nfs/nfs_bio.c:1.192
--- src/sys/nfs/nfs_bio.c:1.191	Wed Jul 15 03:28:55 2015
+++ src/sys/nfs/nfs_bio.c	Fri Dec 13 20:10:21 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: nfs_bio.c,v 1.191 2015/07/15 03:28:55 manu Exp $	*/
+/*	$NetBSD: nfs_bio.c,v 1.192 2019/12/13 20:10:21 ad Exp $	*/
 
 /*
  * Copyright (c) 1989, 1993
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.191 2015/07/15 03:28:55 manu Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nfs_bio.c,v 1.192 2019/12/13 20:10:21 ad Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_nfs.h"
@@ -1340,9 +1340,7 @@ nfs_getpages(void *v)
 				 * available and put back original pgs array.
 				 */
 
-				mutex_enter(&uvm_pageqlock);
 				uvm_page_unbusy(pgs, npages);
-				mutex_exit(&uvm_pageqlock);
 				*ap->a_count = 0;
 				memcpy(pgs, opgs,
 				    npages * sizeof(struct vm_pages *));

Index: src/sys/rump/librump/rumpkern/vm.c
diff -u src/sys/rump/librump/rumpkern/vm.c:1.173 src/sys/rump/librump/rumpkern/vm.c:1.174
--- src/sys/rump/librump/rumpkern/vm.c:1.173	Sun May 14 13:49:55 2017
+++ src/sys/rump/librump/rumpkern/vm.c	Fri Dec 13 20:10:21 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: vm.c,v 1.173 2017/05/14 13:49:55 nat Exp $	*/
+/*	$NetBSD: vm.c,v 1.174 2019/12/13 20:10:21 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.173 2017/05/14 13:49:55 nat Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vm.c,v 1.174 2019/12/13 20:10:21 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/atomic.h>
@@ -67,7 +67,7 @@ __KERNEL_RCSID(0, "$NetBSD: vm.c,v 1.173
 
 #include <rump/rumpuser.h>
 
-kmutex_t uvm_pageqlock; /* non-free page lock */
+kmutex_t vmpage_lruqueue_lock; /* non-free page lock */
 kmutex_t uvm_fpageqlock; /* free page lock, non-gpl license */
 kmutex_t uvm_swap_data_lock;
 
@@ -213,9 +213,9 @@ uvm_pagealloc_strat(struct uvm_object *u
 	 */
 	if (!UVM_OBJ_IS_AOBJ(uobj)) {
 		atomic_inc_uint(&vmpage_onqueue);
-		mutex_enter(&uvm_pageqlock);
+		mutex_enter(&vmpage_lruqueue_lock);
 		TAILQ_INSERT_TAIL(&vmpage_lruqueue, pg, pageq.queue);
-		mutex_exit(&uvm_pageqlock);
+		mutex_exit(&vmpage_lruqueue_lock);
 	}
 
 	uobj->uo_npages++;
@@ -233,7 +233,6 @@ uvm_pagefree(struct vm_page *pg)
 {
 	struct uvm_object *uobj = pg->uobject;
 
-	KASSERT(mutex_owned(&uvm_pageqlock));
 	KASSERT(mutex_owned(uobj->vmobjlock));
 
 	if (pg->flags & PG_WANTED)
@@ -245,7 +244,9 @@ uvm_pagefree(struct vm_page *pg)
 	rb_tree_remove_node(&uobj->rb_tree, pg);
 
 	if (!UVM_OBJ_IS_AOBJ(uobj)) {
+		mutex_enter(&vmpage_lruqueue_lock);
 		TAILQ_REMOVE(&vmpage_lruqueue, pg, pageq.queue);
+		mutex_exit(&vmpage_lruqueue_lock);
 		atomic_dec_uint(&vmpage_onqueue);
 	}
 
@@ -366,7 +367,7 @@ uvm_init(void)
 #endif
 
 	mutex_init(&pagermtx, MUTEX_DEFAULT, IPL_NONE);
-	mutex_init(&uvm_pageqlock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&vmpage_lruqueue_lock, MUTEX_DEFAULT, IPL_NONE);
 	mutex_init(&uvm_swap_data_lock, MUTEX_DEFAULT, IPL_NONE);
 
 	/* just to appease linkage */
@@ -619,10 +620,10 @@ uvm_pagelookup(struct uvm_object *uobj, 
 
 	pg = rb_tree_find_node(&uobj->rb_tree, &off);
 	if (pg && !UVM_OBJ_IS_AOBJ(pg->uobject) && !ispagedaemon) {
-		mutex_enter(&uvm_pageqlock);
+		mutex_enter(&vmpage_lruqueue_lock);
 		TAILQ_REMOVE(&vmpage_lruqueue, pg, pageq.queue);
 		TAILQ_INSERT_TAIL(&vmpage_lruqueue, pg, pageq.queue);
-		mutex_exit(&uvm_pageqlock);
+		mutex_exit(&vmpage_lruqueue_lock);
 	}
 
 	return pg;
@@ -1052,7 +1053,7 @@ processpage(struct vm_page *pg, bool *lo
 	uobj = pg->uobject;
 	if (mutex_tryenter(uobj->vmobjlock)) {
 		if ((pg->flags & PG_BUSY) == 0) {
-			mutex_exit(&uvm_pageqlock);
+			mutex_exit(&vmpage_lruqueue_lock);
 			uobj->pgops->pgo_put(uobj, pg->offset,
 			    pg->offset + PAGE_SIZE,
 			    PGO_CLEANIT|PGO_FREE);
@@ -1130,7 +1131,7 @@ uvm_pageout(void *arg)
 		skip = 0;
 		lockrunning = false;
  again:
-		mutex_enter(&uvm_pageqlock);
+		mutex_enter(&vmpage_lruqueue_lock);
 		while (cleaned < PAGEDAEMON_OBJCHUNK) {
 			skipped = 0;
 			TAILQ_FOREACH(pg, &vmpage_lruqueue, pageq.queue) {
@@ -1153,7 +1154,7 @@ uvm_pageout(void *arg)
 			}
 			break;
 		}
-		mutex_exit(&uvm_pageqlock);
+		mutex_exit(&vmpage_lruqueue_lock);
 
 		/*
 		 * Ok, someone is running with an object lock held.

Index: src/sys/rump/librump/rumpvfs/vm_vfs.c
diff -u src/sys/rump/librump/rumpvfs/vm_vfs.c:1.34 src/sys/rump/librump/rumpvfs/vm_vfs.c:1.35
--- src/sys/rump/librump/rumpvfs/vm_vfs.c:1.34	Fri Oct 18 19:56:11 2013
+++ src/sys/rump/librump/rumpvfs/vm_vfs.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: vm_vfs.c,v 1.34 2013/10/18 19:56:11 christos Exp $	*/
+/*	$NetBSD: vm_vfs.c,v 1.35 2019/12/13 20:10:22 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.34 2013/10/18 19:56:11 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vm_vfs.c,v 1.35 2019/12/13 20:10:22 ad Exp $");
 
 #include <sys/param.h>
 
@@ -73,9 +73,7 @@ uvm_aio_aiodone(struct buf *bp)
 	}
 	KASSERT(mutex_owned(uobj->vmobjlock));
 
-	mutex_enter(&uvm_pageqlock);
 	uvm_page_unbusy(pgs, npages);
-	mutex_exit(&uvm_pageqlock);
 	mutex_exit(uobj->vmobjlock);
 
 	uvm_pagermapout((vaddr_t)bp->b_data, npages);

Index: src/sys/ufs/lfs/lfs_pages.c
diff -u src/sys/ufs/lfs/lfs_pages.c:1.15 src/sys/ufs/lfs/lfs_pages.c:1.16
--- src/sys/ufs/lfs/lfs_pages.c:1.15	Sat Aug 19 14:22:49 2017
+++ src/sys/ufs/lfs/lfs_pages.c	Fri Dec 13 20:10:22 2019
@@ -1,7 +1,7 @@
-/*	$NetBSD: lfs_pages.c,v 1.15 2017/08/19 14:22:49 maya Exp $	*/
+/*	$NetBSD: lfs_pages.c,v 1.16 2019/12/13 20:10:22 ad Exp $	*/
 
 /*-
- * Copyright (c) 1999, 2000, 2001, 2002, 2003 The NetBSD Foundation, Inc.
+ * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2019 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -60,7 +60,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: lfs_pages.c,v 1.15 2017/08/19 14:22:49 maya Exp $");
+__KERNEL_RCSID(0, "$NetBSD: lfs_pages.c,v 1.16 2019/12/13 20:10:22 ad Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_compat_netbsd.h"
@@ -375,9 +375,7 @@ check_dirty(struct lfs *fs, struct vnode
 					 * Wire the page so that
 					 * pdaemon doesn't see it again.
 					 */
-					mutex_enter(&uvm_pageqlock);
 					uvm_pagewire(pg);
-					mutex_exit(&uvm_pageqlock);
 
 					/* Suspended write flag */
 					pg->flags |= PG_DELWRI;
@@ -539,9 +537,7 @@ retry:
 						    "lfsput2", 0);
 				mutex_enter(vp->v_interlock);
 			}
-			mutex_enter(&uvm_pageqlock);
 			uvm_pageactivate(pg);
-			mutex_exit(&uvm_pageqlock);
 		}
 		ap->a_offlo = blkeof;
 		if (ap->a_offhi > 0 && ap->a_offhi <= ap->a_offlo) {
@@ -817,7 +813,6 @@ retry:
 		}
 	
 		busypg = NULL;
-		KASSERT(!mutex_owned(&uvm_pageqlock));
 		oreclaim = (ap->a_flags & PGO_RECLAIM);
 		ap->a_flags &= ~PGO_RECLAIM;
 		error = genfs_do_putpages(vp, startoffset, endoffset,

Index: src/sys/ufs/lfs/lfs_vfsops.c
diff -u src/sys/ufs/lfs/lfs_vfsops.c:1.365 src/sys/ufs/lfs/lfs_vfsops.c:1.366
--- src/sys/ufs/lfs/lfs_vfsops.c:1.365	Tue May 28 08:59:35 2019
+++ src/sys/ufs/lfs/lfs_vfsops.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: lfs_vfsops.c,v 1.365 2019/05/28 08:59:35 msaitoh Exp $	*/
+/*	$NetBSD: lfs_vfsops.c,v 1.366 2019/12/13 20:10:22 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.365 2019/05/28 08:59:35 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: lfs_vfsops.c,v 1.366 2019/12/13 20:10:22 ad Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_lfs.h"
@@ -2054,9 +2054,7 @@ lfs_gop_write(struct vnode *vp, struct v
 			pgs[i]->flags |= PG_PAGEOUT;
 			uvm_pageout_start(1);
 			mutex_enter(vp->v_interlock);
-			mutex_enter(&uvm_pageqlock);
 			uvm_pageunwire(pgs[i]);
-			mutex_exit(&uvm_pageqlock);
 			mutex_exit(vp->v_interlock);
 		}
 	}
@@ -2238,7 +2236,6 @@ lfs_gop_write(struct vnode *vp, struct v
  		      pgs[0]->offset, eof, npages));
 	}
 
-	mutex_enter(&uvm_pageqlock);
 	for (i = 0; i < npages; i++) {
 		pg = pgs[i];
 
@@ -2262,7 +2259,6 @@ lfs_gop_write(struct vnode *vp, struct v
 	}
 	/* uvm_pageunbusy takes care of PG_BUSY, PG_WANTED */
 	uvm_page_unbusy(pgs, npages);
-	mutex_exit(&uvm_pageqlock);
 	mutex_exit(vp->v_interlock);
 	return EAGAIN;
 }

Index: src/sys/ufs/lfs/ulfs_inode.c
diff -u src/sys/ufs/lfs/ulfs_inode.c:1.21 src/sys/ufs/lfs/ulfs_inode.c:1.22
--- src/sys/ufs/lfs/ulfs_inode.c:1.21	Sat Oct 28 00:37:13 2017
+++ src/sys/ufs/lfs/ulfs_inode.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: ulfs_inode.c,v 1.21 2017/10/28 00:37:13 pgoyette Exp $	*/
+/*	$NetBSD: ulfs_inode.c,v 1.22 2019/12/13 20:10:22 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.21 2017/10/28 00:37:13 pgoyette Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ulfs_inode.c,v 1.22 2019/12/13 20:10:22 ad Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_lfs.h"
@@ -234,7 +234,6 @@ ulfs_balloc_range(struct vnode *vp, off_
 
 	GOP_SIZE(vp, off + len, &eob, 0);
 	mutex_enter(uobj->vmobjlock);
-	mutex_enter(&uvm_pageqlock);
 	for (i = 0; i < npages; i++) {
 		KASSERT((pgs[i]->flags & PG_RELEASED) == 0);
 		if (!error) {
@@ -246,7 +245,6 @@ ulfs_balloc_range(struct vnode *vp, off_
 		}
 		uvm_pageactivate(pgs[i]);
 	}
-	mutex_exit(&uvm_pageqlock);
 	uvm_page_unbusy(pgs, npages);
 	mutex_exit(uobj->vmobjlock);
 

Index: src/sys/ufs/ufs/ufs_inode.c
diff -u src/sys/ufs/ufs/ufs_inode.c:1.105 src/sys/ufs/ufs/ufs_inode.c:1.106
--- src/sys/ufs/ufs/ufs_inode.c:1.105	Mon Dec 10 20:48:34 2018
+++ src/sys/ufs/ufs/ufs_inode.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: ufs_inode.c,v 1.105 2018/12/10 20:48:34 jdolecek Exp $	*/
+/*	$NetBSD: ufs_inode.c,v 1.106 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ufs_inode.c,v 1.105 2018/12/10 20:48:34 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ufs_inode.c,v 1.106 2019/12/13 20:10:22 ad Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_ffs.h"
@@ -270,7 +270,6 @@ ufs_balloc_range(struct vnode *vp, off_t
 
 	GOP_SIZE(vp, off + len, &eob, 0);
 	mutex_enter(uobj->vmobjlock);
-	mutex_enter(&uvm_pageqlock);
 	for (i = 0; i < npages; i++) {
 		KASSERT((pgs[i]->flags & PG_RELEASED) == 0);
 		if (!error) {
@@ -282,7 +281,6 @@ ufs_balloc_range(struct vnode *vp, off_t
 		}
 		uvm_pageactivate(pgs[i]);
 	}
-	mutex_exit(&uvm_pageqlock);
 	uvm_page_unbusy(pgs, npages);
 	mutex_exit(uobj->vmobjlock);
 

Index: src/sys/uvm/uvm.h
diff -u src/sys/uvm/uvm.h:1.69 src/sys/uvm/uvm.h:1.70
--- src/sys/uvm/uvm.h:1.69	Sun Dec  1 14:40:31 2019
+++ src/sys/uvm/uvm.h	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm.h,v 1.69 2019/12/01 14:40:31 ad Exp $	*/
+/*	$NetBSD: uvm.h,v 1.70 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -123,7 +123,6 @@ extern struct uvm_object *uvm_kernel_obj
  * locks (made globals for lockstat).
  */
 
-extern kmutex_t uvm_pageqlock;		/* lock for active/inactive page q */
 extern kmutex_t uvm_fpageqlock;		/* lock for free page q */
 extern kmutex_t uvm_kentry_lock;
 

Index: src/sys/uvm/uvm_amap.c
diff -u src/sys/uvm/uvm_amap.c:1.110 src/sys/uvm/uvm_amap.c:1.111
--- src/sys/uvm/uvm_amap.c:1.110	Sun Dec  1 14:24:43 2019
+++ src/sys/uvm/uvm_amap.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_amap.c,v 1.110 2019/12/01 14:24:43 ad Exp $	*/
+/*	$NetBSD: uvm_amap.c,v 1.111 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_amap.c,v 1.110 2019/12/01 14:24:43 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_amap.c,v 1.111 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_uvmhist.h"
 
@@ -966,7 +966,6 @@ amap_copy(struct vm_map *map, struct vm_
  *	map change until we are done copying all the map entrys.
  * => XXXCDC: out of memory should cause fork to fail, but there is
  *	currently no easy way to do this (needs fix)
- * => page queues must be unlocked (we may lock them)
  */
 
 void
@@ -1070,19 +1069,10 @@ ReStart:
 		 * Drop PG_BUSY on new page.  Since its owner was locked all
 		 * this time - it cannot be PG_RELEASED or PG_WANTED.
 		 */
+		uvm_pageactivate(npg);
 		npg->flags &= ~(PG_BUSY|PG_FAKE);
 		UVM_PAGE_OWN(npg, NULL);
 	}
-	/* Activate all pages.  Some may be missing because of retry above. */
-	mutex_enter(&uvm_pageqlock);
-	for (lcv = 0 ; lcv < amap->am_nused ; lcv++) {
-		anon = amap->am_anon[amap->am_slots[lcv]];
-		KASSERT(anon->an_lock == amap->am_lock);
-		if (anon->an_page != NULL) {
-			uvm_pageactivate(anon->an_page);
-		}
-	}
-	mutex_exit(&uvm_pageqlock);
 	amap_unlock(amap);
 }
 

Index: src/sys/uvm/uvm_anon.c
diff -u src/sys/uvm/uvm_anon.c:1.68 src/sys/uvm/uvm_anon.c:1.69
--- src/sys/uvm/uvm_anon.c:1.68	Mon Dec  2 20:02:02 2019
+++ src/sys/uvm/uvm_anon.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_anon.c,v 1.68 2019/12/02 20:02:02 chs Exp $	*/
+/*	$NetBSD: uvm_anon.c,v 1.69 2019/12/13 20:10:22 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.68 2019/12/02 20:02:02 chs Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_anon.c,v 1.69 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_uvmhist.h"
 
@@ -95,7 +95,7 @@ uvm_analloc(void)
 }
 
 /*
- * uvm_anon_dispose: break loans and remove pmap mapping
+ * uvm_anon_dispose: free any resident page or swap resources of anon.
  *
  * => anon must be removed from the amap (if anon was in an amap).
  * => amap must be locked; we may drop and re-acquire the lock here.
@@ -134,11 +134,11 @@ uvm_anon_dispose(struct vm_anon *anon)
 		 */
 
 		if (pg->uobject) {
-			mutex_enter(&uvm_pageqlock);
+			mutex_enter(&pg->interlock);
 			KASSERT(pg->loan_count > 0);
 			pg->loan_count--;
 			pg->uanon = NULL;
-			mutex_exit(&uvm_pageqlock);
+			mutex_exit(&pg->interlock);
 			mutex_exit(pg->uobject->vmobjlock);
 		} else {
 
@@ -160,6 +160,10 @@ uvm_anon_dispose(struct vm_anon *anon)
 				mutex_obj_hold(anon->an_lock);
 				return false;
 			}
+			uvm_pagefree(pg);
+			UVMHIST_LOG(maphist, "anon 0x%#jx, page 0x%#jx: "
+			    "freed now!", (uintptr_t)anon, (uintptr_t)pg,
+			    0, 0);
 		}
 	}
 
@@ -171,6 +175,12 @@ uvm_anon_dispose(struct vm_anon *anon)
 	}
 #endif
 
+	/*
+	 * Free any swap resources, leave a page replacement hint.
+	 */
+
+	uvm_anon_dropswap(anon);
+	uvmpdpol_anfree(anon);
 	UVMHIST_LOG(maphist,"<- done!",0,0,0,0);
 	return true;
 }
@@ -184,10 +194,6 @@ void
 uvm_anon_free(struct vm_anon *anon)
 {
 
-#if defined(VMSWAP)
-	/* Free any dangling swap slot. */
-	uvm_anon_dropswap(anon);
-#endif
 	KASSERT(anon->an_ref == 0);
 	KASSERT(anon->an_lock == NULL);
 	KASSERT(anon->an_page == NULL);
@@ -205,54 +211,22 @@ uvm_anon_free(struct vm_anon *anon)
 void
 uvm_anon_freelst(struct vm_amap *amap, struct vm_anon *anonlst)
 {
-	struct vm_anon *anon;
-	struct vm_anon **anonp = &anonlst;
-	struct vm_page *pg;
+	struct vm_anon *next;
 
 	UVMHIST_FUNC(__func__); UVMHIST_CALLED(maphist);
 
 	KASSERT(mutex_owned(amap->am_lock));
 
-	if (anonlst == NULL) {
-		amap_unlock(amap);
-		return;
-	}
-
-	/* Break loans and hardware mappings.  Defer release of busy pages. */
-	while ((anon = *anonp) != NULL) {
-		if (!uvm_anon_dispose(anon)) {
-			/* Do not free this anon. */
-			*anonp = anon->an_link;
-			/* Note: clears an_ref as well. */
-			anon->an_link = NULL;
-		} else {
-			anonp = &anon->an_link;
-		}
-	}
-
-	/* Free pages and leave a page replacement hint. */
-	mutex_enter(&uvm_pageqlock);
-	for (anon = anonlst; anon != NULL; anon = anon->an_link) {
-		UVMHIST_LOG(maphist, "anon 0x%#jx, page 0x%#jx: "
-		    "releasing now!", (uintptr_t)anon,
-		    (uintptr_t)anon->an_page, 0, 0);
-		if ((pg = anon->an_page) != NULL) {
-			uvm_pagefree(pg);
-		}
-		uvmpdpol_anfree(anon);
-	}
-	mutex_exit(&uvm_pageqlock);
-	amap_unlock(amap);
-
-	/* Free swap space, pages and vm_anon. */
-	while (anonlst) {
-		anon = anonlst->an_link;
+	for (; anonlst != NULL; anonlst = next) {
+		next = anonlst->an_link;
 		/* Note: clears an_ref as well. */
 		anonlst->an_link = NULL;
-		anonlst->an_lock = NULL;
-		uvm_anon_free(anonlst);
-		anonlst = anon;
+		if (uvm_anon_dispose(anonlst)) {
+			anonlst->an_lock = NULL;
+			uvm_anon_free(anonlst);
+		}
 	}
+	amap_unlock(amap);
 }
 
 /*
@@ -276,7 +250,6 @@ struct vm_page *
 uvm_anon_lockloanpg(struct vm_anon *anon)
 {
 	struct vm_page *pg;
-	bool locked = false;
 
 	KASSERT(mutex_owned(anon->an_lock));
 
@@ -290,37 +263,21 @@ uvm_anon_lockloanpg(struct vm_anon *anon
 	 */
 
 	while (((pg = anon->an_page) != NULL) && pg->loan_count != 0) {
-
-		/*
-		 * quickly check to see if the page has an object before
-		 * bothering to lock the page queues.   this may also produce
-		 * a false positive result, but that's ok because we do a real
-		 * check after that.
-		 */
-
+		mutex_enter(&pg->interlock);
 		if (pg->uobject) {
-			mutex_enter(&uvm_pageqlock);
-			if (pg->uobject) {
-				locked =
-				    mutex_tryenter(pg->uobject->vmobjlock);
-			} else {
-				/* object disowned before we got PQ lock */
-				locked = true;
-			}
-			mutex_exit(&uvm_pageqlock);
-
 			/*
 			 * if we didn't get a lock (try lock failed), then we
 			 * toggle our anon lock and try again
 			 */
 
-			if (!locked) {
+			if (!mutex_tryenter(pg->uobject->vmobjlock)) {
 				/*
 				 * someone locking the object has a chance to
 				 * lock us right now
 				 * 
 				 * XXX Better than yielding but inadequate.
 				 */
+				mutex_exit(&pg->interlock);
 				kpause("livelock", false, 1, anon->an_lock);
 				continue;
 			}
@@ -331,12 +288,11 @@ uvm_anon_lockloanpg(struct vm_anon *anon
 		 * then we have to take the ownership.
 		 */
 
-		if (pg->uobject == NULL && (pg->pqflags & PQ_ANON) == 0) {
-			mutex_enter(&uvm_pageqlock);
-			pg->pqflags |= PQ_ANON;
+		if (pg->uobject == NULL && (pg->flags & PG_ANON) == 0) {
+			pg->flags |= PG_ANON;
 			pg->loan_count--;
-			mutex_exit(&uvm_pageqlock);
 		}
+		mutex_exit(&pg->interlock);
 		break;
 	}
 	return pg;
@@ -396,12 +352,7 @@ uvm_anon_pagein(struct vm_amap *amap, st
 	 * Deactivate the page (to put it on a page queue).
 	 */
 
-	mutex_enter(&uvm_pageqlock);
-	if (pg->wire_count == 0) {
-		uvm_pagedeactivate(pg);
-	}
-	mutex_exit(&uvm_pageqlock);
-
+	uvm_pagedeactivate(pg);
 	if (pg->flags & PG_WANTED) {
 		pg->flags &= ~PG_WANTED;
 		wakeup(pg);
@@ -457,9 +408,7 @@ uvm_anon_release(struct vm_anon *anon)
 	KASSERT(pg->loan_count == 0);
 	KASSERT(anon->an_ref == 0);
 
-	mutex_enter(&uvm_pageqlock);
 	uvm_pagefree(pg);
-	mutex_exit(&uvm_pageqlock);
 	KASSERT(anon->an_page == NULL);
 	/* dispose should succeed as no one can reach this anon anymore. */
 	success = uvm_anon_dispose(anon);

Index: src/sys/uvm/uvm_aobj.c
diff -u src/sys/uvm/uvm_aobj.c:1.130 src/sys/uvm/uvm_aobj.c:1.131
--- src/sys/uvm/uvm_aobj.c:1.130	Sun Dec  1 20:31:40 2019
+++ src/sys/uvm/uvm_aobj.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_aobj.c,v 1.130 2019/12/01 20:31:40 ad Exp $	*/
+/*	$NetBSD: uvm_aobj.c,v 1.131 2019/12/13 20:10:22 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.130 2019/12/01 20:31:40 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_aobj.c,v 1.131 2019/12/13 20:10:22 ad Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_uvmhist.h"
@@ -612,26 +612,19 @@ uao_detach(struct uvm_object *uobj)
 	 * involved in is complete), release any swap resources and free
 	 * the page itself.
 	 */
-
 	mutex_enter(uobj->vmobjlock);
-	TAILQ_FOREACH(pg, &uobj->memq, listq.queue) {
-		pmap_page_protect(pg, VM_PROT_NONE);
-	}
-	mutex_enter(&uvm_pageqlock);
 	while ((pg = TAILQ_FIRST(&uobj->memq)) != NULL) {
+		pmap_page_protect(pg, VM_PROT_NONE);
 		if (pg->flags & PG_BUSY) {
 			pg->flags |= PG_WANTED;
-			mutex_exit(&uvm_pageqlock);
 			UVM_UNLOCK_AND_WAIT(pg, uobj->vmobjlock, false,
 			    "uao_det", 0);
 			mutex_enter(uobj->vmobjlock);
-			mutex_enter(&uvm_pageqlock);
 			continue;
 		}
 		uao_dropswap(&aobj->u_obj, pg->offset >> PAGE_SHIFT);
 		uvm_pagefree(pg);
 	}
-	mutex_exit(&uvm_pageqlock);
 
 	/*
 	 * Finally, free the anonymous UVM object itself.
@@ -658,9 +651,6 @@ uao_detach(struct uvm_object *uobj)
  *	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 lock on them [e.g. pagedaemon had
- *	better not call us with the queues locked]
  * => we return 0 unless we encountered some sort of I/O error
  *	XXXJRT currently never happens, as we never directly initiate
  *	XXXJRT I/O
@@ -803,12 +793,7 @@ uao_put(struct uvm_object *uobj, voff_t 
 		case PGO_CLEANIT|PGO_DEACTIVATE:
 		case PGO_DEACTIVATE:
  deactivate_it:
-			mutex_enter(&uvm_pageqlock);
-			/* skip the page if it's wired */
-			if (pg->wire_count == 0) {
-				uvm_pagedeactivate(pg);
-			}
-			mutex_exit(&uvm_pageqlock);
+			uvm_pagedeactivate(pg);
 			break;
 
 		case PGO_FREE:
@@ -833,9 +818,7 @@ uao_put(struct uvm_object *uobj, voff_t 
 			 */
 
 			uao_dropswap(uobj, pg->offset >> PAGE_SHIFT);
-			mutex_enter(&uvm_pageqlock);
 			uvm_pagefree(pg);
-			mutex_exit(&uvm_pageqlock);
 			break;
 
 		default:
@@ -921,7 +904,7 @@ uao_get(struct uvm_object *uobj, voff_t 
 				if (ptmp) {
 					/* new page */
 					ptmp->flags &= ~(PG_FAKE);
-					ptmp->pqflags |= PQ_AOBJ;
+					ptmp->flags |= PG_AOBJ;
 					goto gotpage;
 				}
 			}
@@ -1023,12 +1006,7 @@ gotpage:
 					continue;
 				}
 
-				/*
-				 * safe with PQ's unlocked: because we just
-				 * alloc'd the page
-				 */
-
-				ptmp->pqflags |= PQ_AOBJ;
+				ptmp->flags |= PG_AOBJ;
 
 				/*
 				 * got new page ready for I/O.  break pps while
@@ -1128,9 +1106,7 @@ gotpage:
 					uvm_swap_markbad(swslot, 1);
 				}
 
-				mutex_enter(&uvm_pageqlock);
 				uvm_pagefree(ptmp);
-				mutex_exit(&uvm_pageqlock);
 				mutex_exit(uobj->vmobjlock);
 				return error;
 			}
@@ -1380,10 +1356,7 @@ uao_pagein_page(struct uvm_aobj *aobj, i
 	/*
 	 * make sure it's on a page queue.
 	 */
-	mutex_enter(&uvm_pageqlock);
-	if (pg->wire_count == 0)
-		uvm_pageenqueue(pg);
-	mutex_exit(&uvm_pageqlock);
+	uvm_pageenqueue(pg);
 
 	if (pg->flags & PG_WANTED) {
 		wakeup(pg);

Index: src/sys/uvm/uvm_bio.c
diff -u src/sys/uvm/uvm_bio.c:1.100 src/sys/uvm/uvm_bio.c:1.101
--- src/sys/uvm/uvm_bio.c:1.100	Thu Nov  7 07:45:14 2019
+++ src/sys/uvm/uvm_bio.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_bio.c,v 1.100 2019/11/07 07:45:14 skrll Exp $	*/
+/*	$NetBSD: uvm_bio.c,v 1.101 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1998 Chuck Silvers.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_bio.c,v 1.100 2019/11/07 07:45:14 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_bio.c,v 1.101 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_uvmhist.h"
 #include "opt_ubc.h"
@@ -243,9 +243,7 @@ ubc_fault_page(const struct uvm_faultinf
 	}
 	KASSERT((pg->flags & PG_FAKE) == 0);
 	if (pg->flags & PG_RELEASED) {
-		mutex_enter(&uvm_pageqlock);
 		uvm_pagefree(pg);
-		mutex_exit(&uvm_pageqlock);
 		return 0;
 	}
 	if (pg->loan_count != 0) {
@@ -287,9 +285,7 @@ ubc_fault_page(const struct uvm_faultinf
 	error = pmap_enter(ufi->orig_map->pmap, va, VM_PAGE_TO_PHYS(pg),
 	    prot & mask, PMAP_CANFAIL | (access_type & mask));
 
-	mutex_enter(&uvm_pageqlock);
 	uvm_pageactivate(pg);
-	mutex_exit(&uvm_pageqlock);
 	pg->flags &= ~(PG_BUSY|PG_WANTED);
 	UVM_PAGE_OWN(pg, NULL);
 
@@ -659,7 +655,6 @@ ubc_release(void *va, int flags)
 		}
 		umap->flags &= ~UMAP_PAGES_LOCKED;
 		mutex_enter(uobj->vmobjlock);
-		mutex_enter(&uvm_pageqlock);
 		for (u_int i = 0; i < npages; i++) {
 			paddr_t pa;
 			bool rv __diagused;
@@ -672,7 +667,6 @@ ubc_release(void *va, int flags)
 			KASSERT(pgs[i]->loan_count == 0);
 			uvm_pageactivate(pgs[i]);
 		}
-		mutex_exit(&uvm_pageqlock);
 		pmap_kremove(umapva, ubc_winsize);
 		pmap_update(pmap_kernel());
 		uvm_page_unbusy(pgs, npages);
@@ -891,7 +885,6 @@ ubc_direct_release(struct uvm_object *uo
 	int flags, struct vm_page **pgs, int npages)
 {
 	mutex_enter(uobj->vmobjlock);
-	mutex_enter(&uvm_pageqlock);
 	for (int i = 0; i < npages; i++) {
 		struct vm_page *pg = pgs[i];
 
@@ -901,8 +894,6 @@ ubc_direct_release(struct uvm_object *uo
 		if (flags & UBC_WRITE)
 			pg->flags &= ~(PG_FAKE|PG_CLEAN);
 	}
-	mutex_exit(&uvm_pageqlock);
-
 	uvm_page_unbusy(pgs, npages);
 	mutex_exit(uobj->vmobjlock);
 }

Index: src/sys/uvm/uvm_fault.c
diff -u src/sys/uvm/uvm_fault.c:1.211 src/sys/uvm/uvm_fault.c:1.212
--- src/sys/uvm/uvm_fault.c:1.211	Sun Dec  1 14:30:01 2019
+++ src/sys/uvm/uvm_fault.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_fault.c,v 1.211 2019/12/01 14:30:01 ad Exp $	*/
+/*	$NetBSD: uvm_fault.c,v 1.212 2019/12/13 20:10:22 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.211 2019/12/01 14:30:01 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_fault.c,v 1.212 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_uvmhist.h"
 
@@ -191,19 +191,15 @@ uvmfault_anonflush(struct vm_anon **anon
 	int lcv;
 	struct vm_page *pg;
 
-	mutex_enter(&uvm_pageqlock);
 	for (lcv = 0; lcv < n; lcv++) {
 		if (anons[lcv] == NULL)
 			continue;
 		KASSERT(mutex_owned(anons[lcv]->an_lock));
 		pg = anons[lcv]->an_page;
 		if (pg && (pg->flags & PG_BUSY) == 0) {
-			if (pg->wire_count == 0) {
-				uvm_pagedeactivate(pg);
-			}
+			uvm_pagedeactivate(pg);
 		}
 	}
-	mutex_exit(&uvm_pageqlock);
 }
 
 /*
@@ -453,9 +449,7 @@ uvmfault_anonget(struct uvm_faultinfo *u
 				 * pmap_page_protect() it.
 				 */
 
-				mutex_enter(&uvm_pageqlock);
 				uvm_pagefree(pg);
-				mutex_exit(&uvm_pageqlock);
 
 				if (locked) {
 					uvmfault_unlockall(ufi, NULL, NULL);
@@ -492,9 +486,7 @@ released:
 			 * We have successfully read the page, activate it.
 			 */
 
-			mutex_enter(&uvm_pageqlock);
 			uvm_pageactivate(pg);
-			mutex_exit(&uvm_pageqlock);
 			pg->flags &= ~(PG_WANTED|PG_BUSY|PG_FAKE);
 			UVM_PAGE_OWN(pg, NULL);
 #else
@@ -1272,9 +1264,7 @@ uvm_fault_upper_neighbor(
 
 	/* locked: amap, anon */
 
-	mutex_enter(&uvm_pageqlock);
 	uvm_pageenqueue(pg);
-	mutex_exit(&uvm_pageqlock);
 	UVMHIST_LOG(maphist,
 	    "  MAPPING: n anon: pm=%#jx, va=%#jx, pg=%#jx",
 	    (uintptr_t)ufi->orig_map->pmap, currva, (uintptr_t)pg, 0);
@@ -1492,9 +1482,8 @@ uvm_fault_upper_promote(
 	KASSERT(anon == NULL || anon->an_lock == oanon->an_lock);
 
 	pg = anon->an_page;
-	mutex_enter(&uvm_pageqlock);
-	uvm_pageenqueue(pg); /* uvm_fault_upper_done will activate the page */
-	mutex_exit(&uvm_pageqlock);
+	/* uvm_fault_upper_done will activate the page */
+	uvm_pageenqueue(pg);
 	pg->flags &= ~(PG_BUSY|PG_FAKE);
 	UVM_PAGE_OWN(pg, NULL);
 
@@ -1627,7 +1616,6 @@ uvm_fault_upper_done(
 	 * ... update the page queues.
 	 */
 
-	mutex_enter(&uvm_pageqlock);
 	if (wire_paging) {
 		uvm_pagewire(pg);
 
@@ -1639,11 +1627,9 @@ uvm_fault_upper_done(
 		 */
 
 		pg->flags &= ~(PG_CLEAN);
-
 	} else {
 		uvm_pageactivate(pg);
 	}
-	mutex_exit(&uvm_pageqlock);
 
 	if (wire_paging) {
 		uvm_anon_dropswap(anon);
@@ -1863,9 +1849,7 @@ uvm_fault_lower_neighbor(
 	 * for this.  we can just directly enter the pages.
 	 */
 
-	mutex_enter(&uvm_pageqlock);
 	uvm_pageenqueue(pg);
-	mutex_exit(&uvm_pageqlock);
 	UVMHIST_LOG(maphist,
 	    "  MAPPING: n obj: pm=%#jx, va=%#jx, pg=%#jx",
 	    (uintptr_t)ufi->orig_map->pmap, currva, (uintptr_t)pg, 0);
@@ -1984,9 +1968,7 @@ uvm_fault_lower_io(
 	mutex_enter(uobj->vmobjlock);
 	KASSERT((pg->flags & PG_BUSY) != 0);
 
-	mutex_enter(&uvm_pageqlock);
 	uvm_pageactivate(pg);
-	mutex_exit(&uvm_pageqlock);
 
 	/* locked(locked): maps(read), amap(if !null), uobj, pg */
 	/* locked(!locked): uobj, pg */
@@ -2288,9 +2270,7 @@ uvm_fault_lower_enter(
 		 * we just promoted the page.
 		 */
 
-		mutex_enter(&uvm_pageqlock);
 		uvm_pageenqueue(pg);
-		mutex_exit(&uvm_pageqlock);
 
 		if (pg->flags & PG_WANTED)
 			wakeup(pg);
@@ -2349,10 +2329,9 @@ uvm_fault_lower_done(
 
 	UVMHIST_FUNC("uvm_fault_lower_done"); UVMHIST_CALLED(maphist);
 
-	mutex_enter(&uvm_pageqlock);
 	if (flt->wire_paging) {
 		uvm_pagewire(pg);
-		if (pg->pqflags & PQ_AOBJ) {
+		if (pg->flags & PG_AOBJ) {
 
 			/*
 			 * since the now-wired page cannot be paged out,
@@ -2368,7 +2347,6 @@ uvm_fault_lower_done(
 	} else {
 		uvm_pageactivate(pg);
 	}
-	mutex_exit(&uvm_pageqlock);
 
 	if (dropswap) {
 		uao_dropswap(uobj, pg->offset >> PAGE_SHIFT);
@@ -2481,11 +2459,9 @@ uvm_fault_unwire_locked(struct vm_map *m
 
 		if (entry != oentry) {
 			if (oentry != NULL) {
-				mutex_exit(&uvm_pageqlock);
 				uvm_map_unlock_entry(oentry);
 			}
 			uvm_map_lock_entry(entry);
-			mutex_enter(&uvm_pageqlock);
 			oentry = entry;
 		}
 
@@ -2505,7 +2481,6 @@ uvm_fault_unwire_locked(struct vm_map *m
 	}
 
 	if (oentry != NULL) {
-		mutex_exit(&uvm_pageqlock);
 		uvm_map_unlock_entry(entry);
 	}
 }

Index: src/sys/uvm/uvm_init.c
diff -u src/sys/uvm/uvm_init.c:1.50 src/sys/uvm/uvm_init.c:1.51
--- src/sys/uvm/uvm_init.c:1.50	Sun Dec  1 14:28:01 2019
+++ src/sys/uvm/uvm_init.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_init.c,v 1.50 2019/12/01 14:28:01 ad Exp $	*/
+/*	$NetBSD: uvm_init.c,v 1.51 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_init.c,v 1.50 2019/12/01 14:28:01 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_init.c,v 1.51 2019/12/13 20:10:22 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -64,7 +64,6 @@ const int * const uvmexp_pagemask = &uvm
 const int * const uvmexp_pageshift = &uvmexp.pageshift;
 #endif
 
-kmutex_t uvm_pageqlock __cacheline_aligned;
 kmutex_t uvm_fpageqlock __cacheline_aligned;
 kmutex_t uvm_kentry_lock __cacheline_aligned;
 

Index: src/sys/uvm/uvm_km.c
diff -u src/sys/uvm/uvm_km.c:1.150 src/sys/uvm/uvm_km.c:1.151
--- src/sys/uvm/uvm_km.c:1.150	Sun Dec  1 23:14:47 2019
+++ src/sys/uvm/uvm_km.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_km.c,v 1.150 2019/12/01 23:14:47 uwe Exp $	*/
+/*	$NetBSD: uvm_km.c,v 1.151 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -152,7 +152,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_km.c,v 1.150 2019/12/01 23:14:47 uwe Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_km.c,v 1.151 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_uvmhist.h"
 
@@ -474,9 +474,7 @@ uvm_km_pgremove(vaddr_t startva, vaddr_t
 		}
 		uao_dropswap(uobj, curoff >> PAGE_SHIFT);
 		if (pg != NULL) {
-			mutex_enter(&uvm_pageqlock);
 			uvm_pagefree(pg);
-			mutex_exit(&uvm_pageqlock);
 		}
 	}
 	mutex_exit(uobj->vmobjlock);

Index: src/sys/uvm/uvm_loan.c
diff -u src/sys/uvm/uvm_loan.c:1.88 src/sys/uvm/uvm_loan.c:1.89
--- src/sys/uvm/uvm_loan.c:1.88	Sun Dec  1 14:40:31 2019
+++ src/sys/uvm/uvm_loan.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_loan.c,v 1.88 2019/12/01 14:40:31 ad Exp $	*/
+/*	$NetBSD: uvm_loan.c,v 1.89 2019/12/13 20:10:22 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.88 2019/12/01 14:40:31 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_loan.c,v 1.89 2019/12/13 20:10:22 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -59,7 +59,7 @@ UVMHIST_DEFINE(loanhist);
  * at the same time.
  *
  * loans are tracked by pg->loan_count.  an O->A page will have both
- * a uvm_object and a vm_anon, but PQ_ANON will not be set.   this sort
+ * a uvm_object and a vm_anon, but PG_ANON will not be set.   this sort
  * of page is considered "owned" by the uvm_object (not the anon).
  *
  * each loan of a page to the kernel bumps the pg->wire_count.  the
@@ -77,15 +77,15 @@ UVMHIST_DEFINE(loanhist);
  * pages should be freed when the last loan is dropped.   in some cases
  * an anon may "adopt" an orphaned page.
  *
- * locking: to read pg->loan_count either the owner or the page queues
+ * locking: to read pg->loan_count either the owner or pg->interlock
  * must be locked.   to modify pg->loan_count, both the owner of the page
- * and the PQs must be locked.   pg->flags is (as always) locked by
+ * and pg->interlock must be locked.   pg->flags is (as always) locked by
  * the owner of the page.
  *
  * note that locking from the "loaned" side is tricky since the object
  * getting the loaned page has no reference to the page's owner and thus
  * the owner could "die" at any time.   in order to prevent the owner
- * from dying the page queues should be locked.   this forces us to sometimes
+ * from dying pg->interlock should be locked.   this forces us to sometimes
  * use "try" locking.
  *
  * loans are typically broken by the following events:
@@ -357,7 +357,7 @@ uvm_loananon(struct uvm_faultinfo *ufi, 
 	if (flags & UVM_LOAN_TOANON) {
 		KASSERT(mutex_owned(anon->an_lock));
 		pg = anon->an_page;
-		if (pg && (pg->pqflags & PQ_ANON) != 0 && anon->an_ref == 1) {
+		if (pg && (pg->flags & PG_ANON) != 0 && anon->an_ref == 1) {
 			if (pg->wire_count > 0) {
 				UVMHIST_LOG(loanhist, "->A wired %#jx",
 				    (uintptr_t)pg, 0, 0, 0);
@@ -412,9 +412,7 @@ uvm_loananon(struct uvm_faultinfo *ufi, 
 	 */
 
 	pg = anon->an_page;
-	mutex_enter(&uvm_pageqlock);
 	if (pg->wire_count > 0) {
-		mutex_exit(&uvm_pageqlock);
 		UVMHIST_LOG(loanhist, "->K wired %#jx", (uintptr_t)pg, 0, 0, 0);
 		KASSERT(pg->uobject == NULL);
 		uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, NULL);
@@ -423,10 +421,11 @@ uvm_loananon(struct uvm_faultinfo *ufi, 
 	if (pg->loan_count == 0) {
 		pmap_page_protect(pg, VM_PROT_READ);
 	}
+	mutex_enter(&pg->interlock);
 	pg->loan_count++;
 	KASSERT(pg->loan_count > 0);	/* detect wrap-around */
+	mutex_exit(&pg->interlock);
 	uvm_pageactivate(pg);
-	mutex_exit(&uvm_pageqlock);
 	**output = pg;
 	(*output)++;
 
@@ -463,9 +462,7 @@ uvm_loanpage(struct vm_page **pgpp, int 
 		KASSERT(mutex_owned(pg->uobject->vmobjlock));
 		KASSERT(pg->flags & PG_BUSY);
 
-		mutex_enter(&uvm_pageqlock);
 		if (pg->wire_count > 0) {
-			mutex_exit(&uvm_pageqlock);
 			UVMHIST_LOG(loanhist, "wired %#jx", (uintptr_t)pg,
 			    0, 0, 0);
 			error = EBUSY;
@@ -474,10 +471,11 @@ uvm_loanpage(struct vm_page **pgpp, int 
 		if (pg->loan_count == 0) {
 			pmap_page_protect(pg, VM_PROT_READ);
 		}
+		mutex_enter(&pg->interlock);
 		pg->loan_count++;
 		KASSERT(pg->loan_count > 0);	/* detect wrap-around */
+		mutex_exit(&pg->interlock);
 		uvm_pageactivate(pg);
-		mutex_exit(&uvm_pageqlock);
 	}
 
 	uvm_page_unbusy(pgpp, npages);
@@ -576,9 +574,7 @@ reget:
 					slock = pg->uobject->vmobjlock;
 
 					mutex_enter(slock);
-					mutex_enter(&uvm_pageqlock);
 					uvm_page_unbusy(&pg, 1);
-					mutex_exit(&uvm_pageqlock);
 					mutex_exit(slock);
 				}
 				goto reget;
@@ -713,15 +709,11 @@ uvm_loanuobj(struct uvm_faultinfo *ufi, 
 				wakeup(pg);
 			}
 			if (pg->flags & PG_RELEASED) {
-				mutex_enter(&uvm_pageqlock);
 				uvm_pagefree(pg);
-				mutex_exit(&uvm_pageqlock);
 				mutex_exit(uobj->vmobjlock);
 				return (0);
 			}
-			mutex_enter(&uvm_pageqlock);
 			uvm_pageactivate(pg);
-			mutex_exit(&uvm_pageqlock);
 			pg->flags &= ~(PG_BUSY|PG_WANTED);
 			UVM_PAGE_OWN(pg, NULL);
 			mutex_exit(uobj->vmobjlock);
@@ -779,22 +771,21 @@ uvm_loanuobj(struct uvm_faultinfo *ufi, 
 	if (anon == NULL) {
 		goto fail;
 	}
-	mutex_enter(&uvm_pageqlock);
 	if (pg->wire_count > 0) {
-		mutex_exit(&uvm_pageqlock);
 		UVMHIST_LOG(loanhist, "wired %#jx", (uintptr_t)pg, 0, 0, 0);
 		goto fail;
 	}
 	if (pg->loan_count == 0) {
 		pmap_page_protect(pg, VM_PROT_READ);
 	}
+	mutex_enter(&pg->interlock);
 	pg->loan_count++;
 	KASSERT(pg->loan_count > 0);	/* detect wrap-around */
 	pg->uanon = anon;
 	anon->an_page = pg;
 	anon->an_lock = /* TODO: share amap lock */
+	mutex_exit(&pg->interlock);
 	uvm_pageactivate(pg);
-	mutex_exit(&uvm_pageqlock);
 	if (pg->flags & PG_WANTED) {
 		wakeup(pg);
 	}
@@ -872,17 +863,15 @@ again:
 		/* got a zero'd page. */
 		pg->flags &= ~(PG_WANTED|PG_BUSY|PG_FAKE);
 		pg->flags |= PG_RDONLY;
-		mutex_enter(&uvm_pageqlock);
 		uvm_pageactivate(pg);
-		mutex_exit(&uvm_pageqlock);
 		UVM_PAGE_OWN(pg, NULL);
 	}
 
 	if ((flags & UVM_LOAN_TOANON) == 0) {	/* loaning to kernel-page */
-		mutex_enter(&uvm_pageqlock);
+		mutex_enter(&pg->interlock);
 		pg->loan_count++;
 		KASSERT(pg->loan_count > 0);	/* detect wrap-around */
-		mutex_exit(&uvm_pageqlock);
+		mutex_exit(&pg->interlock);
 		mutex_exit(uvm_loanzero_object.vmobjlock);
 		**output = pg;
 		(*output)++;
@@ -920,11 +909,11 @@ again:
 	}
 	anon->an_page = pg;
 	pg->uanon = anon;
-	mutex_enter(&uvm_pageqlock);
+	mutex_enter(&pg->interlock);
 	pg->loan_count++;
 	KASSERT(pg->loan_count > 0);	/* detect wrap-around */
+	mutex_exit(&pg->interlock);
 	uvm_pageactivate(pg);
-	mutex_exit(&uvm_pageqlock);
 	mutex_exit(&anon->an_lock);
 	mutex_exit(uvm_loanzero_object.vmobjlock);
 	**output = anon;
@@ -973,7 +962,6 @@ uvm_unloanpage(struct vm_page **ploans, 
 	struct vm_page *pg;
 	kmutex_t *slock;
 
-	mutex_enter(&uvm_pageqlock);
 	while (npages-- > 0) {
 		pg = *ploans++;
 
@@ -983,6 +971,7 @@ uvm_unloanpage(struct vm_page **ploans, 
 		 * so we have to do a try-lock here.
 		 */
 
+		mutex_enter(&pg->interlock);
 		slock = NULL;
 		while (pg->uobject != NULL || pg->uanon != NULL) {
 			if (pg->uobject != NULL) {
@@ -994,16 +983,16 @@ uvm_unloanpage(struct vm_page **ploans, 
 				break;
 			}
 			/* XXX Better than yielding but inadequate. */
-			kpause("livelock", false, 1, &uvm_pageqlock);
+			kpause("livelock", false, 1, &pg->interlock);
 			slock = NULL;
 		}
 
 		/*
 		 * drop our loan.  if page is owned by an anon but
-		 * PQ_ANON is not set, the page was loaned to the anon
+		 * PG_ANON is not set, the page was loaned to the anon
 		 * from an object which dropped ownership, so resolve
 		 * this by turning the anon's loan into real ownership
-		 * (ie. decrement loan_count again and set PQ_ANON).
+		 * (ie. decrement loan_count again and set PG_ANON).
 		 * after all this, if there are no loans left, put the
 		 * page back a paging queue (if the page is owned by
 		 * an anon) or free it (if the page is now unowned).
@@ -1012,11 +1001,12 @@ uvm_unloanpage(struct vm_page **ploans, 
 		KASSERT(pg->loan_count > 0);
 		pg->loan_count--;
 		if (pg->uobject == NULL && pg->uanon != NULL &&
-		    (pg->pqflags & PQ_ANON) == 0) {
+		    (pg->flags & PG_ANON) == 0) {
 			KASSERT(pg->loan_count > 0);
 			pg->loan_count--;
-			pg->pqflags |= PQ_ANON;
+			pg->flags |= PG_ANON;
 		}
+		mutex_exit(&pg->interlock);
 		if (pg->loan_count == 0 && pg->uobject == NULL &&
 		    pg->uanon == NULL) {
 			KASSERT((pg->flags & PG_BUSY) == 0);
@@ -1026,7 +1016,6 @@ uvm_unloanpage(struct vm_page **ploans, 
 			mutex_exit(slock);
 		}
 	}
-	mutex_exit(&uvm_pageqlock);
 }
 
 /*
@@ -1075,12 +1064,11 @@ ulz_put(struct uvm_object *uobj, voff_t 
 	KASSERT(pg != NULL);
 	KASSERT(TAILQ_NEXT(pg, listq.queue) == NULL);
 
-	mutex_enter(&uvm_pageqlock);
 	if (pg->uanon)
 		uvm_pageactivate(pg);
-	else
+	else {
 		uvm_pagedequeue(pg);
-	mutex_exit(&uvm_pageqlock);
+	}
 
 	mutex_exit(uobj->vmobjlock);
 	return 0;
@@ -1134,7 +1122,7 @@ uvm_loanbreak(struct vm_page *uobjpage)
 	 * force a reload of the old page by clearing it from all
 	 * pmaps.
 	 * transfer dirtiness of the old page to the new page.
-	 * then lock the page queues to rename the pages.
+	 * then lock pg->interlock to rename the pages.
 	 */
 
 	uvm_pagecopy(uobjpage, pg);	/* old -> new */
@@ -1155,12 +1143,11 @@ uvm_loanbreak(struct vm_page *uobjpage)
 	uobjpage->flags &= ~(PG_WANTED|PG_BUSY);
 	UVM_PAGE_OWN(uobjpage, NULL);
 
-	mutex_enter(&uvm_pageqlock);
-
 	/*
 	 * replace uobjpage with new page.
 	 */
 
+	mutex_enter(&uobjpage->interlock);
 	uvm_pagereplace(uobjpage, pg);
 
 	/*
@@ -1170,15 +1157,14 @@ uvm_loanbreak(struct vm_page *uobjpage)
 	 */
 	if (uobjpage->uanon == NULL)
 		uvm_pagedequeue(uobjpage);
+	mutex_exit(&uobjpage->interlock);
 
 	/*
 	 * at this point we have absolutely no
 	 * control over uobjpage
 	 */
 
-	/* install new page */
 	uvm_pageactivate(pg);
-	mutex_exit(&uvm_pageqlock);
 
 	/*
 	 * done!  loan is broken and "pg" is
@@ -1207,11 +1193,16 @@ uvm_loanbreak_anon(struct vm_anon *anon,
 
 	/* force reload */
 	pmap_page_protect(anon->an_page, VM_PROT_NONE);
-	mutex_enter(&uvm_pageqlock);	  /* KILL loan */
-
+	if (pg < anon->an_page) {
+		mutex_enter(&pg->interlock);
+		mutex_enter(&anon->an_page->interlock);
+	} else {
+		mutex_enter(&anon->an_page->interlock);
+		mutex_enter(&pg->interlock);
+	}
 	anon->an_page->uanon = NULL;
 	/* in case we owned */
-	anon->an_page->pqflags &= ~PQ_ANON;
+	anon->an_page->flags &= ~PG_ANON;
 
 	if (uobj) {
 		/* if we were receiver of loan */
@@ -1224,21 +1215,22 @@ uvm_loanbreak_anon(struct vm_anon *anon,
 		uvm_pagedequeue(anon->an_page);
 	}
 
-	if (uobj) {
-		mutex_exit(uobj->vmobjlock);
-	}
-
 	/* install new page in anon */
 	anon->an_page = pg;
 	pg->uanon = anon;
-	pg->pqflags |= PQ_ANON;
+	pg->flags |= PG_ANON;
 
+	mutex_exit(&pg->interlock);
+	mutex_exit(&anon->an_page->interlock);
 	uvm_pageactivate(pg);
-	mutex_exit(&uvm_pageqlock);
 
 	pg->flags &= ~(PG_BUSY|PG_FAKE);
 	UVM_PAGE_OWN(pg, NULL);
 
+	if (uobj) {
+		mutex_exit(uobj->vmobjlock);
+	}
+
 	/* done! */
 
 	return 0;

Index: src/sys/uvm/uvm_map.c
diff -u src/sys/uvm/uvm_map.c:1.366 src/sys/uvm/uvm_map.c:1.367
--- src/sys/uvm/uvm_map.c:1.366	Fri Nov  1 13:04:22 2019
+++ src/sys/uvm/uvm_map.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_map.c,v 1.366 2019/11/01 13:04:22 rin Exp $	*/
+/*	$NetBSD: uvm_map.c,v 1.367 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.366 2019/11/01 13:04:22 rin Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_map.c,v 1.367 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_ddb.h"
 #include "opt_pax.h"
@@ -3944,15 +3944,12 @@ uvm_map_clean(struct vm_map *map, vaddr_
 				 * at all in these cases.
 				 */
 
-				mutex_enter(&uvm_pageqlock);
 				if (pg->loan_count != 0 ||
 				    pg->wire_count != 0) {
-					mutex_exit(&uvm_pageqlock);
 					continue;
 				}
 				KASSERT(pg->uanon == anon);
 				uvm_pagedeactivate(pg);
-				mutex_exit(&uvm_pageqlock);
 				continue;
 
 			case PGO_FREE:

Index: src/sys/uvm/uvm_object.c
diff -u src/sys/uvm/uvm_object.c:1.15 src/sys/uvm/uvm_object.c:1.16
--- src/sys/uvm/uvm_object.c:1.15	Mon Oct 26 09:02:49 2015
+++ src/sys/uvm/uvm_object.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_object.c,v 1.15 2015/10/26 09:02:49 mrg Exp $	*/
+/*	$NetBSD: uvm_object.c,v 1.16 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 2006, 2010 The NetBSD Foundation, Inc.
@@ -37,7 +37,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_object.c,v 1.15 2015/10/26 09:02:49 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_object.c,v 1.16 2019/12/13 20:10:22 ad Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -173,20 +173,18 @@ uvm_obj_wirepages(struct uvm_object *uob
 				pgs[i] = pg;
 			}
 
-			if (pgs[i]->pqflags & PQ_AOBJ) {
+			if (pgs[i]->flags & PG_AOBJ) {
 				pgs[i]->flags &= ~(PG_CLEAN);
 				uao_dropswap(uobj, i);
 			}
 		}
 
 		/* Wire the pages */
-		mutex_enter(&uvm_pageqlock);
 		for (i = 0; i < npages; i++) {
 			uvm_pagewire(pgs[i]);
 			if (list != NULL)
 				TAILQ_INSERT_TAIL(list, pgs[i], pageq.queue);
 		}
-		mutex_exit(&uvm_pageqlock);
 
 		/* Unbusy the pages */
 		uvm_page_unbusy(pgs, npages);
@@ -219,7 +217,6 @@ uvm_obj_unwirepages(struct uvm_object *u
 	off_t offset;
 
 	mutex_enter(uobj->vmobjlock);
-	mutex_enter(&uvm_pageqlock);
 	for (offset = start; offset < end; offset += PAGE_SIZE) {
 		pg = uvm_pagelookup(uobj, offset);
 
@@ -228,7 +225,6 @@ uvm_obj_unwirepages(struct uvm_object *u
 
 		uvm_pageunwire(pg);
 	}
-	mutex_exit(&uvm_pageqlock);
 	mutex_exit(uobj->vmobjlock);
 }
 

Index: src/sys/uvm/uvm_page.c
diff -u src/sys/uvm/uvm_page.c:1.200 src/sys/uvm/uvm_page.c:1.201
--- src/sys/uvm/uvm_page.c:1.200	Fri Sep 20 11:09:43 2019
+++ src/sys/uvm/uvm_page.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_page.c,v 1.200 2019/09/20 11:09:43 maxv Exp $	*/
+/*	$NetBSD: uvm_page.c,v 1.201 2019/12/13 20:10:22 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.200 2019/09/20 11:09:43 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_page.c,v 1.201 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_ddb.h"
 #include "opt_uvm.h"
@@ -201,7 +201,6 @@ const rb_tree_ops_t uvm_page_tree_ops = 
  * uvm_pageinsert: insert a page in the object.
  *
  * => caller must lock object
- * => caller must lock page queues
  * => call should have already set pg's object and offset pointers
  *    and bumped the version counter
  */
@@ -264,7 +263,6 @@ uvm_pageinsert(struct uvm_object *uobj, 
  * uvm_page_remove: remove page from object.
  *
  * => caller must lock object
- * => caller must lock page queues
  */
 
 static inline void
@@ -346,15 +344,14 @@ uvm_page_init(vaddr_t *kvm_startp, vaddr
 	CTASSERT(sizeof(pagearray->offset) >= sizeof(struct uvm_cpu *));
 
 	/*
-	 * init the page queues and page queue locks, except the free
-	 * list; we allocate that later (with the initial vm_page
+	 * init the page queues and free page queue lock, except the
+	 * free list; we allocate that later (with the initial vm_page
 	 * structures).
 	 */
 
 	uvm.cpus[0] = &boot_cpu;
 	curcpu()->ci_data.cpu_uvm = &boot_cpu;
 	uvmpdpol_init();
-	mutex_init(&uvm_pageqlock, MUTEX_DRIVER, IPL_NONE);
 	mutex_init(&uvm_fpageqlock, MUTEX_DRIVER, IPL_VM);
 
 	/*
@@ -840,7 +837,7 @@ uvm_pagealloc_pgfl(struct uvm_cpu *ucpu,
 		/* cpu, try1 */
 		if ((pg = LIST_FIRST((freeq =
 		    &pgfl->pgfl_buckets[color].pgfl_queues[try1]))) != NULL) {
-			KASSERT(pg->pqflags & PQ_FREE);
+			KASSERT(pg->flags & PG_FREE);
 			KASSERT(try1 == PGFL_ZEROS || !(pg->flags & PG_ZERO));
 			KASSERT(try1 == PGFL_UNKNOWN || (pg->flags & PG_ZERO));
 			KASSERT(ucpu == VM_FREE_PAGE_TO_CPU(pg));
@@ -851,7 +848,7 @@ uvm_pagealloc_pgfl(struct uvm_cpu *ucpu,
 		/* global, try1 */
 		if ((pg = LIST_FIRST((freeq =
 		    &gpgfl->pgfl_buckets[color].pgfl_queues[try1]))) != NULL) {
-			KASSERT(pg->pqflags & PQ_FREE);
+			KASSERT(pg->flags & PG_FREE);
 			KASSERT(try1 == PGFL_ZEROS || !(pg->flags & PG_ZERO));
 			KASSERT(try1 == PGFL_UNKNOWN || (pg->flags & PG_ZERO));
 			KASSERT(ucpu != VM_FREE_PAGE_TO_CPU(pg));
@@ -862,7 +859,7 @@ uvm_pagealloc_pgfl(struct uvm_cpu *ucpu,
 		/* cpu, try2 */
 		if ((pg = LIST_FIRST((freeq =
 		    &pgfl->pgfl_buckets[color].pgfl_queues[try2]))) != NULL) {
-			KASSERT(pg->pqflags & PQ_FREE);
+			KASSERT(pg->flags & PG_FREE);
 			KASSERT(try2 == PGFL_ZEROS || !(pg->flags & PG_ZERO));
 			KASSERT(try2 == PGFL_UNKNOWN || (pg->flags & PG_ZERO));
 			KASSERT(ucpu == VM_FREE_PAGE_TO_CPU(pg));
@@ -873,7 +870,7 @@ uvm_pagealloc_pgfl(struct uvm_cpu *ucpu,
 		/* global, try2 */
 		if ((pg = LIST_FIRST((freeq =
 		    &gpgfl->pgfl_buckets[color].pgfl_queues[try2]))) != NULL) {
-			KASSERT(pg->pqflags & PQ_FREE);
+			KASSERT(pg->flags & PG_FREE);
 			KASSERT(try2 == PGFL_ZEROS || !(pg->flags & PG_ZERO));
 			KASSERT(try2 == PGFL_UNKNOWN || (pg->flags & PG_ZERO));
 			KASSERT(ucpu != VM_FREE_PAGE_TO_CPU(pg));
@@ -1059,21 +1056,30 @@ uvm_pagealloc_strat(struct uvm_object *o
 			ucpu->page_idle_zero = vm_page_zero_enable;
 		}
 	}
-	KASSERT(pg->pqflags == PQ_FREE);
+	KASSERT((pg->flags & ~(PG_ZERO|PG_FREE)) == 0);
 
+	/*
+	 * For now check this - later on we may do lazy dequeue, but need
+	 * to get page.queue used only by the pagedaemon policy first.
+	 */
+	KASSERT(!uvmpdpol_pageisqueued_p(pg));
+
+	/*
+	 * assign the page to the object.  we don't need to lock the page's
+	 * identity to do this, as the caller holds the objects locked, and
+	 * the page is not on any paging queues at this time.
+	 */
 	pg->offset = off;
 	pg->uobject = obj;
 	pg->uanon = anon;
+	KASSERT(uvm_page_locked_p(pg));
 	pg->flags = PG_BUSY|PG_CLEAN|PG_FAKE;
 	if (anon) {
 		anon->an_page = pg;
-		pg->pqflags = PQ_ANON;
+		pg->flags |= PG_ANON;
 		atomic_inc_uint(&uvmexp.anonpages);
-	} else {
-		if (obj) {
-			uvm_pageinsert(obj, pg);
-		}
-		pg->pqflags = 0;
+	} else if (obj) {
+		uvm_pageinsert(obj, pg);
 	}
 	mutex_spin_exit(&uvm_fpageqlock);
 
@@ -1103,6 +1109,7 @@ uvm_pagealloc_strat(struct uvm_object *o
  * uvm_pagereplace: replace a page with another
  *
  * => object must be locked
+ * => interlock must be held
  */
 
 void
@@ -1129,6 +1136,7 @@ uvm_pagereplace(struct vm_page *oldpg, s
  * uvm_pagerealloc: reallocate a page from one object to another
  *
  * => both objects must be locked
+ * => both interlocks must be held
  */
 
 void
@@ -1156,8 +1164,6 @@ uvm_pagerealloc(struct vm_page *pg, stru
 #ifdef DEBUG
 /*
  * check if page is zero-filled
- *
- *  - called with free page queue lock held.
  */
 void
 uvm_pagezerocheck(struct vm_page *pg)
@@ -1196,7 +1202,6 @@ uvm_pagezerocheck(struct vm_page *pg)
  * => erase page's identity (i.e. remove from object)
  * => put page on free list
  * => caller must lock owning object (either anon or uvm_object)
- * => caller must lock page queues
  * => assumes all valid mappings of pg are gone
  */
 
@@ -1206,7 +1211,7 @@ uvm_pagefree(struct vm_page *pg)
 	struct pgflist *pgfl;
 	struct uvm_cpu *ucpu;
 	int index, color, queue;
-	bool iszero;
+	bool iszero, locked;
 
 #ifdef DEBUG
 	if (pg->uobject == (void *)0xdeadbeef &&
@@ -1216,7 +1221,7 @@ uvm_pagefree(struct vm_page *pg)
 #endif /* DEBUG */
 
 	KASSERT((pg->flags & PG_PAGEOUT) == 0);
-	KASSERT(!(pg->pqflags & PQ_FREE));
+	KASSERT(!(pg->flags & PG_FREE));
 	//KASSERT(mutex_owned(&uvm_pageqlock) || !uvmpdpol_pageisqueued_p(pg));
 	KASSERT(pg->uobject == NULL || mutex_owned(pg->uobject->vmobjlock));
 	KASSERT(pg->uobject != NULL || pg->uanon == NULL ||
@@ -1240,15 +1245,17 @@ uvm_pagefree(struct vm_page *pg)
 		 * unbusy the page, and we're done.
 		 */
 
+		mutex_enter(&pg->interlock);
+		locked = true;
 		if (pg->uobject != NULL) {
 			uvm_pageremove(pg->uobject, pg);
 			pg->flags &= ~PG_CLEAN;
 		} else if (pg->uanon != NULL) {
-			if ((pg->pqflags & PQ_ANON) == 0) {
+			if ((pg->flags & PG_ANON) == 0) {
 				pg->loan_count--;
 			} else {
-				pg->pqflags &= ~PQ_ANON;
-				atomic_dec_uint(&uvmexp.anonpages);
+				pg->flags &= ~PG_ANON;
+ 				atomic_dec_uint(&uvmexp.anonpages);
 			}
 			pg->uanon->an_page = NULL;
 			pg->uanon = NULL;
@@ -1262,43 +1269,49 @@ uvm_pagefree(struct vm_page *pg)
 #endif
 		if (pg->loan_count) {
 			KASSERT(pg->uobject == NULL);
+			mutex_exit(&pg->interlock);
 			if (pg->uanon == NULL) {
-				KASSERT(mutex_owned(&uvm_pageqlock));
 				uvm_pagedequeue(pg);
 			}
 			return;
 		}
+	} else if (pg->uobject != NULL || pg->uanon != NULL ||
+	           pg->wire_count != 0) {
+		mutex_enter(&pg->interlock);
+		locked = true;
+	} else {
+		locked = false;
 	}
 
 	/*
 	 * remove page from its object or anon.
 	 */
-
 	if (pg->uobject != NULL) {
 		uvm_pageremove(pg->uobject, pg);
 	} else if (pg->uanon != NULL) {
 		pg->uanon->an_page = NULL;
+		pg->uanon = NULL;
 		atomic_dec_uint(&uvmexp.anonpages);
 	}
 
 	/*
-	 * now remove the page from the queues.
-	 */
-	if (uvmpdpol_pageisqueued_p(pg)) {
-		KASSERT(mutex_owned(&uvm_pageqlock));
-		uvm_pagedequeue(pg);
-	}
-
-	/*
 	 * if the page was wired, unwire it now.
 	 */
 
 	if (pg->wire_count) {
 		pg->wire_count = 0;
-		uvmexp.wired--;
+		atomic_dec_uint(&uvmexp.wired);
+	}
+	if (locked) {
+		mutex_exit(&pg->interlock);
 	}
 
 	/*
+	 * now remove the page from the queues.
+	 */
+	uvm_pagedequeue(pg);
+
+	/*
 	 * and put on free queue
 	 */
 
@@ -1313,7 +1326,7 @@ uvm_pagefree(struct vm_page *pg)
 #endif
 
 	mutex_spin_enter(&uvm_fpageqlock);
-	pg->pqflags = PQ_FREE;
+	pg->flags = PG_FREE;
 
 #ifdef DEBUG
 	if (iszero)
@@ -1348,7 +1361,6 @@ uvm_pagefree(struct vm_page *pg)
  * => pages must either all belong to the same object, or all belong to anons.
  * => if pages are object-owned, object must be locked.
  * => if pages are anon-owned, anons must be locked.
- * => caller must lock page queues if pages may be released.
  * => caller must make sure that anon-owned pages are not PG_RELEASED.
  */
 
@@ -1369,6 +1381,7 @@ uvm_page_unbusy(struct vm_page **pgs, in
 		KASSERT(pg->flags & PG_BUSY);
 		KASSERT((pg->flags & PG_PAGEOUT) == 0);
 		if (pg->flags & PG_WANTED) {
+			/* XXXAD thundering herd problem. */
 			wakeup(pg);
 		}
 		if (pg->flags & PG_RELEASED) {
@@ -1483,8 +1496,8 @@ uvm_pageidlezero(void)
 				LIST_REMOVE(pg, listq.list); /* per-cpu list */
 				ucpu->pages[PGFL_UNKNOWN]--;
 				uvmexp.free--;
-				KASSERT(pg->pqflags == PQ_FREE);
-				pg->pqflags = 0;
+				KASSERT(pg->flags == PG_FREE);
+				pg->flags = 0;
 				mutex_spin_exit(&uvm_fpageqlock);
 #ifdef PMAP_PAGEIDLEZERO
 				if (!PMAP_PAGEIDLEZERO(VM_PAGE_TO_PHYS(pg))) {
@@ -1497,7 +1510,7 @@ uvm_pageidlezero(void)
 					 */
 
 					mutex_spin_enter(&uvm_fpageqlock);
-					pg->pqflags = PQ_FREE;
+					pg->flags = PG_FREE;
 					LIST_INSERT_HEAD(&gpgfl->pgfl_buckets[
 					    nextbucket].pgfl_queues[
 					    PGFL_UNKNOWN], pg, pageq.list);
@@ -1512,15 +1525,13 @@ uvm_pageidlezero(void)
 #else
 				pmap_zero_page(VM_PAGE_TO_PHYS(pg));
 #endif /* PMAP_PAGEIDLEZERO */
-				pg->flags |= PG_ZERO;
-
 				if (!mutex_tryenter(&uvm_fpageqlock)) {
 					lcont = true;
 					mutex_spin_enter(&uvm_fpageqlock);
 				} else {
 					lcont = false;
 				}
-				pg->pqflags = PQ_FREE;
+				pg->flags = PG_FREE | PG_ZERO;
 				LIST_INSERT_HEAD(&gpgfl->pgfl_buckets[
 				    nextbucket].pgfl_queues[PGFL_ZEROS],
 				    pg, pageq.list);
@@ -1567,24 +1578,27 @@ uvm_pagelookup(struct uvm_object *obj, v
 /*
  * uvm_pagewire: wire the page, thus removing it from the daemon's grasp
  *
- * => caller must lock page queues
+ * => caller must lock objects
  */
 
 void
 uvm_pagewire(struct vm_page *pg)
 {
-	KASSERT(mutex_owned(&uvm_pageqlock));
+
+	KASSERT(uvm_page_locked_p(pg));
 #if defined(READAHEAD_STATS)
-	if ((pg->pqflags & PQ_READAHEAD) != 0) {
+	if ((pg->flags & PG_READAHEAD) != 0) {
 		uvm_ra_hit.ev_count++;
-		pg->pqflags &= ~PQ_READAHEAD;
+		pg->flags &= ~PG_READAHEAD;
 	}
 #endif /* defined(READAHEAD_STATS) */
 	if (pg->wire_count == 0) {
 		uvm_pagedequeue(pg);
-		uvmexp.wired++;
+		atomic_inc_uint(&uvmexp.wired);
 	}
+	mutex_enter(&pg->interlock);
 	pg->wire_count++;
+	mutex_exit(&pg->interlock);
 	KASSERT(pg->wire_count > 0);	/* detect wraparound */
 }
 
@@ -1592,26 +1606,30 @@ uvm_pagewire(struct vm_page *pg)
  * uvm_pageunwire: unwire the page.
  *
  * => activate if wire count goes to zero.
- * => caller must lock page queues
+ * => caller must lock objects
  */
 
 void
 uvm_pageunwire(struct vm_page *pg)
 {
-	KASSERT(mutex_owned(&uvm_pageqlock));
+
+	KASSERT(uvm_page_locked_p(pg));
 	KASSERT(pg->wire_count != 0);
+	KASSERT(!uvmpdpol_pageisqueued_p(pg));
+	mutex_enter(&pg->interlock);
 	pg->wire_count--;
+	mutex_exit(&pg->interlock);
 	if (pg->wire_count == 0) {
 		uvm_pageactivate(pg);
 		KASSERT(uvmexp.wired != 0);
-		uvmexp.wired--;
+		atomic_dec_uint(&uvmexp.wired);
 	}
 }
 
 /*
  * uvm_pagedeactivate: deactivate page
  *
- * => caller must lock page queues
+ * => caller must lock objects
  * => caller must check to make sure page is not wired
  * => object that page belongs to must be locked (so we can adjust pg->flags)
  * => caller must clear the reference on the page before calling
@@ -1621,65 +1639,64 @@ void
 uvm_pagedeactivate(struct vm_page *pg)
 {
 
-	KASSERT(mutex_owned(&uvm_pageqlock));
 	KASSERT(uvm_page_locked_p(pg));
-	KASSERT(pg->wire_count != 0 || uvmpdpol_pageisqueued_p(pg));
-	uvmpdpol_pagedeactivate(pg);
+	if (pg->wire_count == 0) {
+		KASSERT(uvmpdpol_pageisqueued_p(pg));
+		uvmpdpol_pagedeactivate(pg);
+	}
 }
 
 /*
  * uvm_pageactivate: activate page
  *
- * => caller must lock page queues
+ * => caller must lock objects
  */
 
 void
 uvm_pageactivate(struct vm_page *pg)
 {
 
-	KASSERT(mutex_owned(&uvm_pageqlock));
 	KASSERT(uvm_page_locked_p(pg));
 #if defined(READAHEAD_STATS)
-	if ((pg->pqflags & PQ_READAHEAD) != 0) {
+	if ((pg->flags & PG_READAHEAD) != 0) {
 		uvm_ra_hit.ev_count++;
-		pg->pqflags &= ~PQ_READAHEAD;
+		pg->flags &= ~PG_READAHEAD;
 	}
 #endif /* defined(READAHEAD_STATS) */
-	if (pg->wire_count != 0) {
-		return;
+	if (pg->wire_count == 0) {
+		uvmpdpol_pageactivate(pg);
 	}
-	uvmpdpol_pageactivate(pg);
 }
 
 /*
  * uvm_pagedequeue: remove a page from any paging queue
+ * 
+ * => caller must lock objects
  */
-
 void
 uvm_pagedequeue(struct vm_page *pg)
 {
 
+	KASSERT(uvm_page_locked_p(pg));
 	if (uvmpdpol_pageisqueued_p(pg)) {
-		KASSERT(mutex_owned(&uvm_pageqlock));
+		uvmpdpol_pagedequeue(pg);
 	}
-
-	uvmpdpol_pagedequeue(pg);
 }
 
 /*
  * uvm_pageenqueue: add a page to a paging queue without activating.
  * used where a page is not really demanded (yet).  eg. read-ahead
+ *
+ * => caller must lock objects
  */
-
 void
 uvm_pageenqueue(struct vm_page *pg)
 {
 
-	KASSERT(mutex_owned(&uvm_pageqlock));
-	if (pg->wire_count != 0) {
-		return;
+	KASSERT(uvm_page_locked_p(pg));
+	if (pg->wire_count == 0 && !uvmpdpol_pageisqueued_p(pg)) {
+		uvmpdpol_pageenqueue(pg);
 	}
-	uvmpdpol_pageenqueue(pg);
 }
 
 /*
@@ -1806,7 +1823,6 @@ uvm_direct_process(struct vm_page **pgs,
  */
 
 static const char page_flagbits[] = UVM_PGFLAGBITS;
-static const char page_pqflagbits[] = UVM_PQFLAGBITS;
 
 void
 uvm_page_printit(struct vm_page *pg, bool full,
@@ -1816,13 +1832,11 @@ uvm_page_printit(struct vm_page *pg, boo
 	struct uvm_object *uobj;
 	struct pgflist *pgl;
 	char pgbuf[128];
-	char pqbuf[128];
 
 	(*pr)("PAGE %p:\n", pg);
 	snprintb(pgbuf, sizeof(pgbuf), page_flagbits, pg->flags);
-	snprintb(pqbuf, sizeof(pqbuf), page_pqflagbits, pg->pqflags);
-	(*pr)("  flags=%s, pqflags=%s, wire_count=%d, pa=0x%lx\n",
-	    pgbuf, pqbuf, pg->wire_count, (long)VM_PAGE_TO_PHYS(pg));
+	(*pr)("  flags=%s, 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);
 #if defined(UVM_PAGE_TRKOWN)
@@ -1839,8 +1853,8 @@ uvm_page_printit(struct vm_page *pg, boo
 		return;
 
 	/* cross-verify object/anon */
-	if ((pg->pqflags & PQ_FREE) == 0) {
-		if (pg->pqflags & PQ_ANON) {
+	if ((pg->flags & PG_FREE) == 0) {
+		if (pg->flags & PG_ANON) {
 			if (pg->uanon == NULL || pg->uanon->an_page != pg)
 			    (*pr)("  >>> ANON DOES NOT POINT HERE <<< (%p)\n",
 				(pg->uanon) ? pg->uanon->an_page : NULL);
@@ -1864,7 +1878,7 @@ uvm_page_printit(struct vm_page *pg, boo
 	}
 
 	/* cross-verify page queue */
-	if (pg->pqflags & PQ_FREE) {
+	if (pg->flags & PG_FREE) {
 		int fl = uvm_page_lookup_freelist(pg);
 		int color = VM_PGCOLOR_BUCKET(pg);
 		pgl = &uvm.page_free[fl].pgfl_buckets[color].pgfl_queues[
@@ -1888,7 +1902,7 @@ uvm_page_printit(struct vm_page *pg, boo
 }
 
 /*
- * uvm_pages_printthem - print a summary of all managed pages
+ * uvm_page_printall - print a summary of all managed pages
  */
 
 void
@@ -1911,7 +1925,7 @@ uvm_page_printall(void (*pr)(const char 
 		     pfn++) {
 			pg = PHYS_TO_VM_PAGE(ptoa(pfn));
 
-			(*pr)("%18p %04x %04x %18p %18p",
+			(*pr)("%18p %04x %08x %18p %18p",
 			    pg, pg->flags, pg->pqflags, pg->uobject,
 			    pg->uanon);
 #ifdef UVM_PAGE_TRKOWN

Index: src/sys/uvm/uvm_page.h
diff -u src/sys/uvm/uvm_page.h:1.84 src/sys/uvm/uvm_page.h:1.85
--- src/sys/uvm/uvm_page.h:1.84	Mon Jan  7 22:48:01 2019
+++ src/sys/uvm/uvm_page.h	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_page.h,v 1.84 2019/01/07 22:48:01 jdolecek Exp $	*/
+/*	$NetBSD: uvm_page.h,v 1.85 2019/12/13 20:10:22 ad Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -100,13 +100,19 @@
  *
  * f:	free page queue lock, uvm_fpageqlock
  * o:	page owner (uvm_object::vmobjlock, vm_amap::am_lock, vm_anon::an_lock)
- * p:	page queue lock, uvm_pageqlock
- * o,p:	o|p for read, o&p for write
+ * i:	vm_page::interlock
+ *        => flags set and cleared only with o&i held can
+ *           safely be tested for with only o held.
+ * o,i:	o|i for read, o&i for write (depends on context - if could be loaned)
+ *	  => see uvm_loan.c
  * w:	wired page queue or uvm_pglistalloc:
- *	  => wired page queue: o&p to change, stable from wire to unwire
+ *	  => wired page queue: o&i to change, stable from wire to unwire
  *		XXX What about concurrent or nested wire?
  *	  => uvm_pglistalloc: owned by caller
  * ?:	locked by pmap or assumed page owner's lock
+ * p:	locked by pagedaemon policy module (pdpolicy)
+ * c:	cpu private
+ * s:	stable, does not change
  *
  * UVM and pmap(9) may use uvm_page_locked_p() to assert whether the
  * page owner's lock is acquired.
@@ -118,7 +124,7 @@
  *   => listq.list is entry on per-CPU free page queue
  *   => uanon is unused (or (void *)0xdeadbeef for DEBUG)
  *   => uobject is unused (or (void *)0xdeadbeef for DEBUG)
- *   => PQ_FREE is set in pqflags
+ *   => 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
@@ -129,7 +135,7 @@
  *   => listq is unused (XXX correct?)
  *   => uanon is owner
  *   => uobject is NULL
- *   => PQ_ANON is set in pqflags
+ *   => PG_ANON is set in flags
  * o allocated by uvm_pglistalloc
  *   => pageq.queue is entry on resulting pglist, owned by caller
  *   => listq is unused (XXX correct?)
@@ -158,14 +164,16 @@ struct vm_page {
 		LIST_ENTRY(vm_page) list;	/* f: CPU free page queue */
 	} listq;
 
-	struct vm_anon		*uanon;		/* o,p: anon */
-	struct uvm_object	*uobject;	/* o,p: object */
-	voff_t			offset;		/* o,p: offset into object */
+	struct vm_anon		*uanon;		/* o,i: anon */
+	struct uvm_object	*uobject;	/* o,i: object */
+	voff_t			offset;		/* o: offset into object */
 	uint16_t		flags;		/* o: object flags */
-	uint16_t		loan_count;	/* o,p: num. active loans */
-	uint16_t		wire_count;	/* p: wired down map refs */
-	uint16_t		pqflags;	/* p: page queue flags */
-	paddr_t			phys_addr;	/* physical address of page */
+	uint16_t		spare;		/*  : spare for now */
+	uint32_t		pqflags;	/* p: pdpolicy queue flags */
+	uint32_t		loan_count;	/* o,i: num. active loans */
+	uint32_t		wire_count;	/* o,i: wired down map refs */
+	paddr_t			phys_addr;	/* o: physical address of pg */
+	kmutex_t		interlock;	/* s: lock on identity */
 
 #ifdef __HAVE_VM_PAGE_MD
 	struct vm_page_md	mdpage;		/* ?: pmap-specific data */
@@ -185,8 +193,10 @@ struct vm_page {
  * Locking notes:
  *
  * PG_, struct vm_page::flags	=> locked by owner
- * PQ_, struct vm_page::pqflags	=> locked by uvm_pageqlock
- * PQ_FREE			=> additionally locked by uvm_fpageqlock
+ * PG_AOBJ			=> additionally locked by vm_page::interlock
+ * PG_ANON			=> additionally locked by vm_page::interlock
+ * PG_FREE			=> additionally locked by uvm_fpageqlock
+ *				   for uvm_pglistalloc()
  *
  * Flag descriptions:
  *
@@ -246,36 +256,20 @@ struct vm_page {
 #define	PG_RELEASED	0x0020
 #define	PG_FAKE		0x0040
 #define	PG_RDONLY	0x0080
-#define	PG_ZERO		0x0100
-#define	PG_MARKER	0x0200
-
-#define PG_PAGER1	0x1000		/* pager-specific flag */
+#define PG_AOBJ		0x0100		/* page is part of an anonymous
+					   uvm_object */
+#define PG_ANON		0x0200		/* 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	UVM_PGFLAGBITS \
 	"\20\1BUSY\2WANTED\3TABLED\4CLEAN\5PAGEOUT\6RELEASED\7FAKE\10RDONLY" \
-	"\11ZERO\12MARKER\15PAGER1"
-
-#define PQ_FREE		0x0001		/* page is on free list */
-#define PQ_ANON		0x0002		/* page is part of an anon, rather
-					   than an uvm_object */
-#define PQ_AOBJ		0x0004		/* page is part of an anonymous
-					   uvm_object */
-#define PQ_SWAPBACKED	(PQ_ANON|PQ_AOBJ)
-#define PQ_READAHEAD	0x0008	/* read-ahead but has not been "hit" yet */
-
-#define PQ_PRIVATE1	0x0100
-#define PQ_PRIVATE2	0x0200
-#define PQ_PRIVATE3	0x0400
-#define PQ_PRIVATE4	0x0800
-#define PQ_PRIVATE5	0x1000
-#define PQ_PRIVATE6	0x2000
-#define PQ_PRIVATE7	0x4000
-#define PQ_PRIVATE8	0x8000
-
-#define	UVM_PQFLAGBITS \
-	"\20\1FREE\2ANON\3AOBJ\4READAHEAD" \
-	"\11PRIVATE1\12PRIVATE2\13PRIVATE3\14PRIVATE4" \
-	"\15PRIVATE5\16PRIVATE6\17PRIVATE7\20PRIVATE8"
+	"\11AOBJ\12AOBJ\13READAHEAD\14FREE\15MARKER\16PAGER1\17ZERO"
 
 /*
  * physical memory layout structure
@@ -362,7 +356,7 @@ int uvm_direct_process(struct vm_page **
 
 #define	PHYS_TO_VM_PAGE(pa)	uvm_phys_to_vm_page(pa)
 
-#define VM_PAGE_IS_FREE(entry)  ((entry)->pqflags & PQ_FREE)
+#define VM_PAGE_IS_FREE(entry)  ((entry)->flags & PG_FREE)
 #define	VM_FREE_PAGE_TO_CPU(pg)	((struct uvm_cpu *)((uintptr_t)pg->offset))
 
 #ifdef DEBUG

Index: src/sys/uvm/uvm_pager.c
diff -u src/sys/uvm/uvm_pager.c:1.113 src/sys/uvm/uvm_pager.c:1.114
--- src/sys/uvm/uvm_pager.c:1.113	Sun Dec  1 23:14:47 2019
+++ src/sys/uvm/uvm_pager.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_pager.c,v 1.113 2019/12/01 23:14:47 uwe Exp $	*/
+/*	$NetBSD: uvm_pager.c,v 1.114 2019/12/13 20:10:22 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.113 2019/12/01 23:14:47 uwe Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_pager.c,v 1.114 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_uvmhist.h"
 #include "opt_readahead.h"
@@ -318,12 +318,11 @@ uvm_aio_aiodone_pages(struct vm_page **p
 	uobj = NULL;
 	pg = pgs[0];
 	swap = (pg->uanon != NULL && pg->uobject == NULL) ||
-		(pg->pqflags & PQ_AOBJ) != 0;
+		(pg->flags & PG_AOBJ) != 0;
 	if (!swap) {
 		uobj = pg->uobject;
 		slock = uobj->vmobjlock;
 		mutex_enter(slock);
-		mutex_enter(&uvm_pageqlock);
 	} else {
 #if defined(VMSWAP)
 		if (error) {
@@ -362,7 +361,6 @@ uvm_aio_aiodone_pages(struct vm_page **p
 				slock = pg->uanon->an_lock;
 			}
 			mutex_enter(slock);
-			mutex_enter(&uvm_pageqlock);
 			anon_disposed = (pg->flags & PG_RELEASED) != 0;
 			KASSERT(!anon_disposed || pg->uobject != NULL ||
 			    pg->uanon->an_ref == 0);
@@ -421,7 +419,7 @@ uvm_aio_aiodone_pages(struct vm_page **p
 			KASSERT(!write);
 			pg->flags &= ~PG_FAKE;
 #if defined(READAHEAD_STATS)
-			pg->pqflags |= PQ_READAHEAD;
+			pg->flags |= PG_READAHEAD;
 			uvm_ra_total.ev_count++;
 #endif /* defined(READAHEAD_STATS) */
 			KASSERT((pg->flags & PG_CLEAN) != 0);
@@ -437,7 +435,7 @@ uvm_aio_aiodone_pages(struct vm_page **p
 		if (pg->flags & PG_PAGEOUT) {
 			pg->flags &= ~PG_PAGEOUT;
 			pageout_done++;
-			uvmexp.pdfreed++;
+			atomic_inc_uint(&uvmexp.pdfreed);
 			pg->flags |= PG_RELEASED;
 		}
 
@@ -448,11 +446,9 @@ uvm_aio_aiodone_pages(struct vm_page **p
 
 		if (swap) {
 			if (pg->uobject == NULL && anon_disposed) {
-				mutex_exit(&uvm_pageqlock);
 				uvm_anon_release(pg->uanon);
 			} else {
 				uvm_page_unbusy(&pg, 1);
-				mutex_exit(&uvm_pageqlock);
 				mutex_exit(slock);
 			}
 		}
@@ -461,7 +457,6 @@ uvm_aio_aiodone_pages(struct vm_page **p
 	uvm_pageout_done(pageout_done);
 	if (!swap) {
 		uvm_page_unbusy(pgs, npages);
-		mutex_exit(&uvm_pageqlock);
 		mutex_exit(slock);
 	} else {
 #if defined(VMSWAP)
@@ -478,7 +473,7 @@ uvm_aio_aiodone_pages(struct vm_page **p
 			else
 				uvm_swap_free(swslot, npages);
 		}
-		uvmexp.pdpending--;
+		atomic_dec_uint(&uvmexp.pdpending);
 #endif /* defined(VMSWAP) */
 	}
 }

Index: src/sys/uvm/uvm_pdaemon.c
diff -u src/sys/uvm/uvm_pdaemon.c:1.112 src/sys/uvm/uvm_pdaemon.c:1.113
--- src/sys/uvm/uvm_pdaemon.c:1.112	Sun Dec  1 14:40:31 2019
+++ src/sys/uvm/uvm_pdaemon.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_pdaemon.c,v 1.112 2019/12/01 14:40:31 ad Exp $	*/
+/*	$NetBSD: uvm_pdaemon.c,v 1.113 2019/12/13 20:10:22 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.112 2019/12/01 14:40:31 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon.c,v 1.113 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_uvmhist.h"
 #include "opt_readahead.h"
@@ -97,7 +97,7 @@ UVMHIST_DEFINE(pdhist);
 
 #define	UVMPD_NUMDIRTYREACTS	16
 
-#define	UVMPD_NUMTRYLOCKOWNER	16
+#define	UVMPD_NUMTRYLOCKOWNER	128
 
 /*
  * local prototypes
@@ -112,7 +112,7 @@ static void	uvmpd_pool_drain_wakeup(void
 static unsigned int uvm_pagedaemon_waiters;
 
 /* State for the pool drainer thread */
-static kmutex_t uvmpd_pool_drain_lock;
+static kmutex_t uvmpd_pool_drain_lock __cacheline_aligned;
 static kcondvar_t uvmpd_pool_drain_cv;
 static bool uvmpd_pool_drain_run = false;
 
@@ -198,7 +198,6 @@ uvm_kick_pdaemon(void)
  * uvmpd_tune: tune paging parameters
  *
  * => called when ever memory is added (or removed?) to the system
- * => caller must call with page queues locked
  */
 
 static void
@@ -260,10 +259,8 @@ uvm_pageout(void *arg)
 	 */
 
 	uvm.pagedaemon_lwp = curlwp;
-	mutex_enter(&uvm_pageqlock);
 	npages = uvmexp.npages;
 	uvmpd_tune();
-	mutex_exit(&uvm_pageqlock);
 
 	/*
 	 * main loop
@@ -287,10 +284,9 @@ uvm_pageout(void *arg)
 		}
 
 		/*
-		 * now lock page queues and recompute inactive count
+		 * now recompute inactive count
 		 */
 
-		mutex_enter(&uvm_pageqlock);
 		if (npages != uvmexp.npages || extrapages != uvm_extrapages) {
 			npages = uvmexp.npages;
 			extrapages = uvm_extrapages;
@@ -334,12 +330,7 @@ uvm_pageout(void *arg)
 		mutex_spin_exit(&uvm_fpageqlock);
 
 		/*
-		 * scan done.  unlock page queues (the only lock we are holding)
-		 */
-		mutex_exit(&uvm_pageqlock);
-
-		/*
-		 * if we don't need free memory, we're done.
+		 * scan done.  if we don't need free memory, we're done.
 		 */
 
 		if (!needsfree && !kmem_va_starved)
@@ -377,23 +368,21 @@ void
 uvm_pageout_start(int npages)
 {
 
-	mutex_spin_enter(&uvm_fpageqlock);
-	uvmexp.paging += npages;
-	mutex_spin_exit(&uvm_fpageqlock);
+	atomic_add_int(&uvmexp.paging, npages);
 }
 
 void
 uvm_pageout_done(int npages)
 {
 
-	mutex_spin_enter(&uvm_fpageqlock);
 	KASSERT(uvmexp.paging >= npages);
-	uvmexp.paging -= npages;
+	atomic_add_int(&uvmexp.paging, -npages);
 
 	/*
 	 * wake up either of pagedaemon or LWPs waiting for it.
 	 */
 
+	mutex_spin_enter(&uvm_fpageqlock);
 	if (uvmexp.free <= uvmexp.reserve_kernel) {
 		wakeup(&uvm.pagedaemon);
 	} else {
@@ -406,7 +395,7 @@ uvm_pageout_done(int npages)
 /*
  * uvmpd_trylockowner: trylock the page's owner.
  *
- * => called with pageq locked.
+ * => called with page interlock held.
  * => resolve orphaned O->A loaned page.
  * => return the locked mutex on success.  otherwise, return NULL.
  */
@@ -415,38 +404,70 @@ kmutex_t *
 uvmpd_trylockowner(struct vm_page *pg)
 {
 	struct uvm_object *uobj = pg->uobject;
+	struct vm_anon *anon = pg->uanon;
+	int tries, count;
+	bool running;
 	kmutex_t *slock;
 
-	KASSERT(mutex_owned(&uvm_pageqlock));
+	KASSERT(mutex_owned(&pg->interlock));
 
 	if (uobj != NULL) {
 		slock = uobj->vmobjlock;
-	} else {
-		struct vm_anon *anon = pg->uanon;
-
-		KASSERT(anon != NULL);
+		KASSERTMSG(slock != NULL, "pg %p uobj %p, NULL lock", pg, uobj);
+	} else if (anon != NULL) {
 		slock = anon->an_lock;
-	}
-
-	if (!mutex_tryenter(slock)) {
+		KASSERTMSG(slock != NULL, "pg %p anon %p, NULL lock", pg, anon);
+	} else {
+		/* Page may be in state of flux - ignore. */
+		mutex_exit(&pg->interlock);
 		return NULL;
 	}
 
-	if (uobj == NULL) {
-
-		/*
-		 * set PQ_ANON if it isn't set already.
-		 */
-
-		if ((pg->pqflags & PQ_ANON) == 0) {
-			KASSERT(pg->loan_count > 0);
-			pg->loan_count--;
-			pg->pqflags |= PQ_ANON;
-			/* anon now owns it */
+	/*
+	 * Now try to lock the objects.  We'll try hard, but don't really
+	 * plan on spending more than a millisecond or so here.
+	 */
+	tries = (curlwp == uvm.pagedaemon_lwp ? UVMPD_NUMTRYLOCKOWNER : 1);
+	for (;;) {
+		if (mutex_tryenter(slock)) {
+			if (uobj == NULL) {
+				/*
+				 * set PG_ANON if it isn't set already.
+				 */
+				if ((pg->flags & PG_ANON) == 0) {
+					KASSERT(pg->loan_count > 0);
+					pg->loan_count--;
+					pg->flags |= PG_ANON;
+					/* anon now owns it */
+				}
+			}
+			mutex_exit(&pg->interlock);
+			return slock;
+		}
+		running = mutex_owner_running(slock);
+		if (!running || --tries <= 0) {
+			break;
 		}
+		count = SPINLOCK_BACKOFF_MAX;
+		SPINLOCK_BACKOFF(count);
 	}
 
-	return slock;
+	/*
+	 * We didn't get the lock; chances are the very next page on the
+	 * queue also has the same lock, so if the lock owner is not running
+	 * take a breather and allow them to make progress.  There could be
+	 * only 1 CPU in the system, or the pagedaemon could have preempted
+	 * the owner in kernel, or any number of other things could be going
+	 * on.
+	 */
+	mutex_exit(&pg->interlock);
+	if (curlwp == uvm.pagedaemon_lwp) {
+		if (!running) {
+			(void)kpause("pdpglock", false, 1, NULL);
+		}
+		uvmexp.pdbusy++;
+	}
+	return NULL;
 }
 
 #if defined(VMSWAP)
@@ -497,7 +518,7 @@ swapcluster_add(struct swapcluster *swc,
 
 	KASSERT(swc->swc_slot != 0);
 	KASSERT(swc->swc_nused < swc->swc_nallocated);
-	KASSERT((pg->pqflags & PQ_SWAPBACKED) != 0);
+	KASSERT((pg->flags & PG_SWAPBACKED) != 0);
 
 	slot = swc->swc_slot + swc->swc_nused;
 	uobj = pg->uobject;
@@ -588,12 +609,12 @@ uvmpd_dropswap(struct vm_page *pg)
 	bool result = false;
 	struct vm_anon *anon = pg->uanon;
 
-	if ((pg->pqflags & PQ_ANON) && anon->an_swslot) {
+	if ((pg->flags & PG_ANON) && anon->an_swslot) {
 		uvm_swap_free(anon->an_swslot, 1);
 		anon->an_swslot = 0;
 		pg->flags &= ~PG_CLEAN;
 		result = true;
-	} else if (pg->pqflags & PQ_AOBJ) {
+	} else if (pg->flags & PG_AOBJ) {
 		int slot = uao_set_swslot(pg->uobject,
 		    pg->offset >> PAGE_SHIFT, 0);
 		if (slot) {
@@ -610,6 +631,7 @@ uvmpd_dropswap(struct vm_page *pg)
  * uvmpd_trydropswap: try to free any swap allocated to this page.
  *
  * => return true if a slot is successfully freed.
+ * => page interlock must be held, and will be dropped.
  */
 
 bool
@@ -619,11 +641,13 @@ uvmpd_trydropswap(struct vm_page *pg)
 	bool result;
 
 	if ((pg->flags & PG_BUSY) != 0) {
+		mutex_exit(&pg->interlock);
 		return false;
 	}
 
 	/*
 	 * lock the page's owner.
+	 * this will drop pg->interlock.
 	 */
 
 	slock = uvmpd_trylockowner(pg);
@@ -653,7 +677,6 @@ uvmpd_trydropswap(struct vm_page *pg)
  * uvmpd_scan_queue: scan an replace candidate list for pages
  * to clean or free.
  *
- * => called with page queues locked
  * => we work on meeting our free target by converting inactive pages
  *    into free pages.
  * => we handle the building of swap-backed clusters
@@ -669,7 +692,6 @@ uvmpd_scan_queue(void)
 	struct swapcluster swc;
 #endif /* defined(VMSWAP) */
 	int dirtyreacts;
-	int lockownerfail;
 	kmutex_t *slock;
 	UVMHIST_FUNC("uvmpd_scan_queue"); UVMHIST_CALLED(pdhist);
 
@@ -684,7 +706,6 @@ uvmpd_scan_queue(void)
 #endif /* defined(VMSWAP) */
 
 	dirtyreacts = 0;
-	lockownerfail = 0;
 	uvmpdpol_scaninit();
 
 	while (/* CONSTCOND */ 1) {
@@ -704,22 +725,9 @@ uvmpd_scan_queue(void)
 			break;
 		}
 
-		p = uvmpdpol_selectvictim();
-		if (p == NULL) {
-			break;
-		}
-		KASSERT(uvmpdpol_pageisqueued_p(p));
-		KASSERT(p->wire_count == 0);
-
-		/*
-		 * we are below target and have a new page to consider.
-		 */
-
-		anon = p->uanon;
-		uobj = p->uobject;
-
 		/*
-		 * first we attempt to lock the object that this page
+		 * first we have the pdpolicy select a victim page
+		 * and attempt to lock the object that the page
 		 * belongs to.  if our attempt fails we skip on to
 		 * the next page (no harm done).  it is important to
 		 * "try" locking the object as we are locking in the
@@ -727,31 +735,28 @@ uvmpd_scan_queue(void)
 		 * deadlock.
 		 *
 		 * the only time we expect to see an ownerless page
-		 * (i.e. a page with no uobject and !PQ_ANON) is if an
+		 * (i.e. a page with no uobject and !PG_ANON) is if an
 		 * anon has loaned a page from a uvm_object and the
 		 * uvm_object has dropped the ownership.  in that
 		 * case, the anon can "take over" the loaned page
 		 * and make it its own.
 		 */
 
-		slock = uvmpd_trylockowner(p);
-		if (slock == NULL) {
-			/*
-			 * yield cpu to make a chance for an LWP holding
-			 * the lock run.  otherwise we can busy-loop too long
-			 * if the page queue is filled with a lot of pages
-			 * from few objects.
-			 */
-			lockownerfail++;
-			if (lockownerfail > UVMPD_NUMTRYLOCKOWNER) {
-				mutex_exit(&uvm_pageqlock);
-				/* XXX Better than yielding but inadequate. */
-				kpause("livelock", false, 1, NULL);
-				mutex_enter(&uvm_pageqlock);
-				lockownerfail = 0;
-			}
-			continue;
+		p = uvmpdpol_selectvictim(&slock);
+		if (p == NULL) {
+			break;
 		}
+		KASSERT(uvmpdpol_pageisqueued_p(p));
+		KASSERT(uvm_page_locked_p(p));
+		KASSERT(p->wire_count == 0);
+
+		/*
+		 * we are below target and have a new page to consider.
+		 */
+
+		anon = p->uanon;
+		uobj = p->uobject;
+
 		if (p->flags & PG_BUSY) {
 			mutex_exit(slock);
 			uvmexp.pdbusy++;
@@ -772,24 +777,22 @@ uvmpd_scan_queue(void)
 
 
 		/*
-		 * we now have the object and the page queues locked.
+		 * we now have the object locked.
 		 * if the page is not swap-backed, call the object's
 		 * pager to flush and free the page.
 		 */
 
 #if defined(READAHEAD_STATS)
-		if ((p->pqflags & PQ_READAHEAD) != 0) {
-			p->pqflags &= ~PQ_READAHEAD;
+		if ((p->flags & PG_READAHEAD) != 0) {
+			p->flags &= ~PG_READAHEAD;
 			uvm_ra_miss.ev_count++;
 		}
 #endif /* defined(READAHEAD_STATS) */
 
-		if ((p->pqflags & PQ_SWAPBACKED) == 0) {
+		if ((p->flags & PG_SWAPBACKED) == 0) {
 			KASSERT(uobj != NULL);
-			mutex_exit(&uvm_pageqlock);
 			(void) (uobj->pgops->pgo_put)(uobj, p->offset,
 			    p->offset + PAGE_SIZE, PGO_CLEANIT|PGO_FREE);
-			mutex_enter(&uvm_pageqlock);
 			continue;
 		}
 
@@ -810,7 +813,7 @@ uvmpd_scan_queue(void)
 
 			pageidx = p->offset >> PAGE_SHIFT;
 			uvm_pagefree(p);
-			uvmexp.pdfreed++;
+			atomic_inc_uint(&uvmexp.pdfreed);
 
 			/*
 			 * for anons, we need to remove the page
@@ -877,12 +880,10 @@ uvmpd_scan_queue(void)
 
 		p->flags |= PG_BUSY;
 		UVM_PAGE_OWN(p, "scan_queue");
-
 		p->flags |= PG_PAGEOUT;
-		uvm_pagedequeue(p);
-
 		uvmexp.pgswapout++;
-		mutex_exit(&uvm_pageqlock);
+
+		uvm_pagedequeue(p);
 
 		/*
 		 * add the new page to the cluster.
@@ -891,7 +892,6 @@ uvmpd_scan_queue(void)
 		if (swapcluster_add(&swc, p)) {
 			p->flags &= ~(PG_BUSY|PG_PAGEOUT);
 			UVM_PAGE_OWN(p, NULL);
-			mutex_enter(&uvm_pageqlock);
 			dirtyreacts++;
 			uvm_pageactivate(p);
 			mutex_exit(slock);
@@ -900,14 +900,13 @@ uvmpd_scan_queue(void)
 		mutex_exit(slock);
 
 		swapcluster_flush(&swc, false);
-		mutex_enter(&uvm_pageqlock);
 
 		/*
 		 * the pageout is in progress.  bump counters and set up
 		 * for the next loop.
 		 */
 
-		uvmexp.pdpending++;
+		atomic_inc_uint(&uvmexp.pdpending);
 
 #else /* defined(VMSWAP) */
 		uvm_pageactivate(p);
@@ -916,16 +915,12 @@ uvmpd_scan_queue(void)
 	}
 
 #if defined(VMSWAP)
-	mutex_exit(&uvm_pageqlock);
 	swapcluster_flush(&swc, true);
-	mutex_enter(&uvm_pageqlock);
 #endif /* defined(VMSWAP) */
 }
 
 /*
  * uvmpd_scan: scan the page queues and attempt to meet our targets.
- *
- * => called with pageq's locked
  */
 
 static void

Index: src/sys/uvm/uvm_pdpolicy.h
diff -u src/sys/uvm/uvm_pdpolicy.h:1.3 src/sys/uvm/uvm_pdpolicy.h:1.4
--- src/sys/uvm/uvm_pdpolicy.h:1.3	Wed Feb 21 23:00:14 2007
+++ src/sys/uvm/uvm_pdpolicy.h	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_pdpolicy.h,v 1.3 2007/02/21 23:00:14 thorpej Exp $	*/
+/*	$NetBSD: uvm_pdpolicy.h,v 1.4 2019/12/13 20:10:22 ad Exp $	*/
 
 /*-
  * Copyright (c)2005, 2006 YAMAMOTO Takashi,
@@ -51,7 +51,7 @@ void uvmpdpol_anfree(struct vm_anon *);
 
 void uvmpdpol_tune(void);
 void uvmpdpol_scaninit(void);
-struct vm_page *uvmpdpol_selectvictim(void);
+struct vm_page *uvmpdpol_selectvictim(kmutex_t **lock);
 void uvmpdpol_balancequeue(int);
 
 void uvmpdpol_sysctlsetup(void);

Index: src/sys/uvm/uvm_pdpolicy_clock.c
diff -u src/sys/uvm/uvm_pdpolicy_clock.c:1.17 src/sys/uvm/uvm_pdpolicy_clock.c:1.18
--- src/sys/uvm/uvm_pdpolicy_clock.c:1.17	Mon Jan 30 17:21:52 2012
+++ src/sys/uvm/uvm_pdpolicy_clock.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_pdpolicy_clock.c,v 1.17 2012/01/30 17:21:52 para Exp $	*/
+/*	$NetBSD: uvm_pdpolicy_clock.c,v 1.18 2019/12/13 20:10:22 ad Exp $	*/
 /*	NetBSD: uvm_pdaemon.c,v 1.72 2006/01/05 10:47:33 yamt Exp $	*/
 
 /*
@@ -69,7 +69,7 @@
 #else /* defined(PDSIM) */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_pdpolicy_clock.c,v 1.17 2012/01/30 17:21:52 para Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_pdpolicy_clock.c,v 1.18 2019/12/13 20:10:22 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/proc.h>
@@ -79,18 +79,23 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_pdpolicy
 #include <uvm/uvm.h>
 #include <uvm/uvm_pdpolicy.h>
 #include <uvm/uvm_pdpolicy_impl.h>
+#include <uvm/uvm_stat.h>
 
 #endif /* defined(PDSIM) */
 
-#define PQ_INACTIVE	PQ_PRIVATE1	/* page is in inactive list */
-#define PQ_ACTIVE	PQ_PRIVATE2	/* page is in active list */
+#define	PQ_TIME		0x3fffffff	/* time of last activation */
+#define PQ_INACTIVE	0x40000000	/* page is in inactive list */
+#define PQ_ACTIVE	0x80000000	/* page is in active list */
 
 #if !defined(CLOCK_INACTIVEPCT)
 #define	CLOCK_INACTIVEPCT	33
 #endif /* !defined(CLOCK_INACTIVEPCT) */
 
 struct uvmpdpol_globalstate {
-	struct pglist s_activeq;	/* allocated pages, in use */
+	kmutex_t lock;			/* lock on state */
+					/* <= compiler pads here */
+	struct pglist s_activeq		/* allocated pages, in use */
+	    __aligned(COHERENCY_UNIT);
 	struct pglist s_inactiveq;	/* pages between the clock hands */
 	int s_active;
 	int s_inactive;
@@ -110,7 +115,11 @@ struct uvmpdpol_scanstate {
 	struct vm_page *ss_nextpg;
 };
 
-static struct uvmpdpol_globalstate pdpol_state;
+static void	uvmpdpol_pageactivate_locked(struct vm_page *);
+static void	uvmpdpol_pagedeactivate_locked(struct vm_page *);
+static void	uvmpdpol_pagedequeue_locked(struct vm_page *);
+
+static struct uvmpdpol_globalstate pdpol_state __cacheline_aligned;
 static struct uvmpdpol_scanstate pdpol_scanstate;
 
 PDPOL_EVCNT_DEFINE(reactexec)
@@ -144,6 +153,7 @@ uvmpdpol_scaninit(void)
 	 * to keep usage within the minimum and maximum usage limits.
 	 */
 
+	mutex_enter(&s->lock);
 	t = s->s_active + s->s_inactive + uvmexp.free;
 	anonunder = uvmexp.anonpages <= UVM_PCTPARAM_APPLY(&s->s_anonmin, t);
 	fileunder = uvmexp.filepages <= UVM_PCTPARAM_APPLY(&s->s_filemin, t);
@@ -162,17 +172,18 @@ uvmpdpol_scaninit(void)
 	ss->ss_execreact = execreact;
 
 	ss->ss_first = true;
+	mutex_exit(&s->lock);
 }
 
 struct vm_page *
-uvmpdpol_selectvictim(void)
+uvmpdpol_selectvictim(kmutex_t **plock)
 {
+	struct uvmpdpol_globalstate *s = &pdpol_state;
 	struct uvmpdpol_scanstate *ss = &pdpol_scanstate;
 	struct vm_page *pg;
 	kmutex_t *lock;
 
-	KASSERT(mutex_owned(&uvm_pageqlock));
-
+	mutex_enter(&s->lock);
 	while (/* CONSTCOND */ 1) {
 		struct vm_anon *anon;
 		struct uvm_object *uobj;
@@ -190,28 +201,24 @@ uvmpdpol_selectvictim(void)
 			break;
 		}
 		ss->ss_nextpg = TAILQ_NEXT(pg, pageq.queue);
+		KASSERT(pg->wire_count == 0);
 
 		uvmexp.pdscans++;
 
 		/*
-		 * move referenced pages back to active queue and
-		 * skip to next page.
+		 * acquire interlock to stablize page identity.
+		 * if we have caught the page in a state of flux
+		 * and it should be dequeued, do it now and then
+		 * move on to the next.
 		 */
-
-		lock = uvmpd_trylockowner(pg);
-		if (lock != NULL) {
-			if (pmap_is_referenced(pg)) {
-				uvmpdpol_pageactivate(pg);
-				uvmexp.pdreact++;
-				mutex_exit(lock);
-				continue;
-			}
-			mutex_exit(lock);
+		mutex_enter(&pg->interlock);
+	        if ((pg->uobject == NULL && pg->uanon == NULL) ||
+	            pg->wire_count > 0) {
+	            	mutex_exit(&pg->interlock);
+	            	uvmpdpol_pagedequeue_locked(pg);
+	            	continue;
 		}
 
-		anon = pg->uanon;
-		uobj = pg->uobject;
-
 		/*
 		 * enforce the minimum thresholds on different
 		 * types of memory usage.  if reusing the current
@@ -219,33 +226,74 @@ uvmpdpol_selectvictim(void)
 		 * minimum, reactivate the page instead and move
 		 * on to the next page.
 		 */
-
+		anon = pg->uanon;
+		uobj = pg->uobject;
 		if (uobj && UVM_OBJ_IS_VTEXT(uobj) && ss->ss_execreact) {
-			uvmpdpol_pageactivate(pg);
+			mutex_exit(&pg->interlock);
+			uvmpdpol_pageactivate_locked(pg);
 			PDPOL_EVCNT_INCR(reactexec);
 			continue;
 		}
 		if (uobj && UVM_OBJ_IS_VNODE(uobj) &&
 		    !UVM_OBJ_IS_VTEXT(uobj) && ss->ss_filereact) {
-			uvmpdpol_pageactivate(pg);
+			mutex_exit(&pg->interlock);
+			uvmpdpol_pageactivate_locked(pg);
 			PDPOL_EVCNT_INCR(reactfile);
 			continue;
 		}
 		if ((anon || UVM_OBJ_IS_AOBJ(uobj)) && ss->ss_anonreact) {
-			uvmpdpol_pageactivate(pg);
+			mutex_exit(&pg->interlock);
+			uvmpdpol_pageactivate_locked(pg);
 			PDPOL_EVCNT_INCR(reactanon);
 			continue;
 		}
 
+		/*
+		 * try to lock the object that owns the page.
+		 *
+		 * with the page interlock held, we can drop s->lock, which
+		 * could otherwise serve as a barrier to us getting the
+		 * object locked, because the owner of the object's lock may
+		 * be blocked on s->lock (i.e. a deadlock).
+		 *
+		 * whatever happens, uvmpd_trylockowner() will release the
+		 * interlock.  with the interlock dropped we can then
+		 * re-acquire our own lock.  the order is:
+		 *
+		 *	object -> pdpol -> interlock.
+	         */
+	        mutex_exit(&s->lock);
+        	lock = uvmpd_trylockowner(pg);
+        	/* pg->interlock now released */
+        	mutex_enter(&s->lock);
+		if (lock == NULL) {
+			/* didn't get it - try the next page. */
+			continue;
+		}
+
+		/*
+		 * move referenced pages back to active queue and skip to
+		 * next page.
+		 */
+		if (pmap_is_referenced(pg)) {
+			uvmpdpol_pageactivate_locked(pg);
+			uvmexp.pdreact++;
+			mutex_exit(lock);
+			continue;
+		}
+
+		/* we have a potential victim. */
+		*plock = lock;
 		break;
 	}
-
+	mutex_exit(&s->lock);
 	return pg;
 }
 
 void
 uvmpdpol_balancequeue(int swap_shortage)
 {
+	struct uvmpdpol_globalstate *s = &pdpol_state;
 	int inactive_shortage;
 	struct vm_page *p, *nextpg;
 	kmutex_t *lock;
@@ -255,6 +303,7 @@ uvmpdpol_balancequeue(int swap_shortage)
 	 * our inactive target.
 	 */
 
+	mutex_enter(&s->lock);
 	inactive_shortage = pdpol_state.s_inactarg - pdpol_state.s_inactive;
 	for (p = TAILQ_FIRST(&pdpol_state.s_activeq);
 	     p != NULL && (inactive_shortage > 0 || swap_shortage > 0);
@@ -265,10 +314,14 @@ uvmpdpol_balancequeue(int swap_shortage)
 		 * if there's a shortage of swap slots, try to free it.
 		 */
 
-		if (swap_shortage > 0 && (p->pqflags & PQ_SWAPBACKED) != 0) {
+		if (swap_shortage > 0 && (p->flags & PG_SWAPBACKED) != 0) {
+			mutex_enter(&p->interlock);
+			mutex_exit(&s->lock);
 			if (uvmpd_trydropswap(p)) {
 				swap_shortage--;
 			}
+			/* p->interlock now released */
+			mutex_enter(&s->lock);
 		}
 
 		/*
@@ -279,27 +332,42 @@ uvmpdpol_balancequeue(int swap_shortage)
 			continue;
 		}
 
-		/* no need to check wire_count as pg is "active" */
+		/*
+		 * acquire interlock to stablize page identity.
+		 * if we have caught the page in a state of flux
+		 * and it should be dequeued, do it now and then
+		 * move on to the next.
+		 */
+		mutex_enter(&p->interlock);
+	        if ((p->uobject == NULL && p->uanon == NULL) ||
+	            p->wire_count > 0) {
+	            	mutex_exit(&p->interlock);
+	            	uvmpdpol_pagedequeue_locked(p);
+	            	continue;
+		}
+		mutex_exit(&s->lock);
 		lock = uvmpd_trylockowner(p);
+		/* p->interlock now released */
+		mutex_enter(&s->lock);
 		if (lock != NULL) {
-			uvmpdpol_pagedeactivate(p);
+			uvmpdpol_pagedeactivate_locked(p);
 			uvmexp.pddeact++;
 			inactive_shortage--;
 			mutex_exit(lock);
 		}
 	}
+	mutex_exit(&s->lock);
 }
 
-void
-uvmpdpol_pagedeactivate(struct vm_page *pg)
+static void
+uvmpdpol_pagedeactivate_locked(struct vm_page *pg)
 {
 
 	KASSERT(uvm_page_locked_p(pg));
-	KASSERT(mutex_owned(&uvm_pageqlock));
 
 	if (pg->pqflags & PQ_ACTIVE) {
 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue);
-		pg->pqflags &= ~PQ_ACTIVE;
+		pg->pqflags &= ~(PQ_ACTIVE | PQ_TIME);
 		KASSERT(pdpol_state.s_active > 0);
 		pdpol_state.s_active--;
 	}
@@ -313,27 +381,49 @@ uvmpdpol_pagedeactivate(struct vm_page *
 }
 
 void
-uvmpdpol_pageactivate(struct vm_page *pg)
+uvmpdpol_pagedeactivate(struct vm_page *pg)
 {
+	struct uvmpdpol_globalstate *s = &pdpol_state;
+
+	mutex_enter(&s->lock);
+	uvmpdpol_pagedeactivate_locked(pg);
+	mutex_exit(&s->lock);
+}
 
-	uvmpdpol_pagedequeue(pg);
+static void
+uvmpdpol_pageactivate_locked(struct vm_page *pg)
+{
+
+	uvmpdpol_pagedequeue_locked(pg);
 	TAILQ_INSERT_TAIL(&pdpol_state.s_activeq, pg, pageq.queue);
-	pg->pqflags |= PQ_ACTIVE;
+	pg->pqflags = PQ_ACTIVE | (hardclock_ticks & PQ_TIME);
 	pdpol_state.s_active++;
 }
 
 void
-uvmpdpol_pagedequeue(struct vm_page *pg)
+uvmpdpol_pageactivate(struct vm_page *pg)
+{
+	struct uvmpdpol_globalstate *s = &pdpol_state;
+
+	/* Safety: PQ_ACTIVE clear also tells us if it is not enqueued. */
+	if ((pg->pqflags & PQ_ACTIVE) == 0 ||
+	    ((hardclock_ticks & PQ_TIME) - (pg->pqflags & PQ_TIME)) > hz) {
+		mutex_enter(&s->lock);
+		uvmpdpol_pageactivate_locked(pg);
+		mutex_exit(&s->lock);
+	}
+}
+
+static void
+uvmpdpol_pagedequeue_locked(struct vm_page *pg)
 {
 
 	if (pg->pqflags & PQ_ACTIVE) {
-		KASSERT(mutex_owned(&uvm_pageqlock));
 		TAILQ_REMOVE(&pdpol_state.s_activeq, pg, pageq.queue);
-		pg->pqflags &= ~PQ_ACTIVE;
+		pg->pqflags &= ~(PQ_ACTIVE | PQ_TIME);
 		KASSERT(pdpol_state.s_active > 0);
 		pdpol_state.s_active--;
 	} else if (pg->pqflags & PQ_INACTIVE) {
-		KASSERT(mutex_owned(&uvm_pageqlock));
 		TAILQ_REMOVE(&pdpol_state.s_inactiveq, pg, pageq.queue);
 		pg->pqflags &= ~PQ_INACTIVE;
 		KASSERT(pdpol_state.s_inactive > 0);
@@ -342,10 +432,23 @@ uvmpdpol_pagedequeue(struct vm_page *pg)
 }
 
 void
+uvmpdpol_pagedequeue(struct vm_page *pg)
+{
+	struct uvmpdpol_globalstate *s = &pdpol_state;
+
+	mutex_enter(&s->lock);
+	uvmpdpol_pagedequeue_locked(pg);
+	mutex_exit(&s->lock);
+}
+
+void
 uvmpdpol_pageenqueue(struct vm_page *pg)
 {
+	struct uvmpdpol_globalstate *s = &pdpol_state;
 
-	uvmpdpol_pageactivate(pg);
+	mutex_enter(&s->lock);
+	uvmpdpol_pageactivate_locked(pg);
+	mutex_exit(&s->lock);
 }
 
 void
@@ -357,19 +460,23 @@ bool
 uvmpdpol_pageisqueued_p(struct vm_page *pg)
 {
 
+	/* Safe to test unlocked due to page life-cycle. */
 	return (pg->pqflags & (PQ_ACTIVE | PQ_INACTIVE)) != 0;
 }
 
 void
 uvmpdpol_estimatepageable(int *active, int *inactive)
 {
+	struct uvmpdpol_globalstate *s = &pdpol_state;
 
+	mutex_enter(&s->lock);
 	if (active) {
 		*active = pdpol_state.s_active;
 	}
 	if (inactive) {
 		*inactive = pdpol_state.s_inactive;
 	}
+	mutex_exit(&s->lock);
 }
 
 #if !defined(PDSIM)
@@ -400,6 +507,7 @@ uvmpdpol_init(void)
 {
 	struct uvmpdpol_globalstate *s = &pdpol_state;
 
+	mutex_init(&s->lock, MUTEX_DEFAULT, IPL_NONE);
 	TAILQ_INIT(&s->s_activeq);
 	TAILQ_INIT(&s->s_inactiveq);
 	uvm_pctparam_init(&s->s_inactivepct, CLOCK_INACTIVEPCT, NULL);
@@ -420,14 +528,18 @@ bool
 uvmpdpol_needsscan_p(void)
 {
 
+	/* This must be an unlocked check: can be called from interrupt. */
 	return pdpol_state.s_inactive < pdpol_state.s_inactarg;
 }
 
 void
 uvmpdpol_tune(void)
 {
+	struct uvmpdpol_globalstate *s = &pdpol_state;
 
+	mutex_enter(&s->lock);
 	clock_tune();
+	mutex_exit(&s->lock);
 }
 
 #if !defined(PDSIM)
Index: src/sys/uvm/uvm_pdpolicy_clockpro.c
diff -u src/sys/uvm/uvm_pdpolicy_clockpro.c:1.17 src/sys/uvm/uvm_pdpolicy_clockpro.c:1.18
--- src/sys/uvm/uvm_pdpolicy_clockpro.c:1.17	Mon Jun 20 23:18:58 2011
+++ src/sys/uvm/uvm_pdpolicy_clockpro.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_pdpolicy_clockpro.c,v 1.17 2011/06/20 23:18:58 yamt Exp $	*/
+/*	$NetBSD: uvm_pdpolicy_clockpro.c,v 1.18 2019/12/13 20:10:22 ad Exp $	*/
 
 /*-
  * Copyright (c)2005, 2006 YAMAMOTO Takashi,
@@ -43,7 +43,7 @@
 #else /* defined(PDSIM) */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_pdpolicy_clockpro.c,v 1.17 2011/06/20 23:18:58 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_pdpolicy_clockpro.c,v 1.18 2019/12/13 20:10:22 ad Exp $");
 
 #include "opt_ddb.h"
 
@@ -121,16 +121,13 @@ PDPOL_EVCNT_DEFINE(speculativemiss)
 PDPOL_EVCNT_DEFINE(locksuccess)
 PDPOL_EVCNT_DEFINE(lockfail)
 
-#define	PQ_REFERENCED	PQ_PRIVATE1
-#define	PQ_HOT		PQ_PRIVATE2
-#define	PQ_TEST		PQ_PRIVATE3
-#define	PQ_INITIALREF	PQ_PRIVATE4
-#if PQ_PRIVATE6 != PQ_PRIVATE5 * 2 || PQ_PRIVATE7 != PQ_PRIVATE6 * 2
-#error PQ_PRIVATE
-#endif
-#define	PQ_QMASK	(PQ_PRIVATE5|PQ_PRIVATE6|PQ_PRIVATE7)
-#define	PQ_QFACTOR	PQ_PRIVATE5
-#define	PQ_SPECULATIVE	PQ_PRIVATE8
+#define	PQ_REFERENCED	0x000000001
+#define	PQ_HOT		0x000000002
+#define	PQ_TEST		0x000000004
+#define	PQ_INITIALREF	0x000000008
+#define	PQ_QMASK	0x000000070
+#define	PQ_QFACTOR	0x000000010
+#define	PQ_SPECULATIVE	0x000000080
 
 #define	CLOCKPRO_NOQUEUE	0
 #define	CLOCKPRO_NEWQ		1	/* small queue to clear initial ref. */
@@ -170,6 +167,7 @@ typedef struct {
 } pageq_t;
 
 struct clockpro_state {
+	kmutex_t lock;
 	int s_npages;
 	int s_coldtarget;
 	int s_ncold;
@@ -203,7 +201,7 @@ clockpro_switchqueue(void)
 
 #endif /* !defined(LISTQ) */
 
-static struct clockpro_state clockpro;
+static struct clockpro_state clockpro __cacheline_aligned;
 static struct clockpro_scanstate {
 	int ss_nscanned;
 } scanstate;
@@ -585,6 +583,8 @@ static void
 clockpro_reinit(void)
 {
 
+	KASSERT(mutex_owned(&clockpro.lock));
+
 	clockpro_hashinit(uvmexp.npages);
 }
 
@@ -594,6 +594,7 @@ clockpro_init(void)
 	struct clockpro_state *s = &clockpro;
 	int i;
 
+	mutex_init(&s->lock, MUTEX_DEFAULT, IPL_NONE);
 	for (i = 0; i < CLOCKPRO_NQUEUE; i++) {
 		pageq_init(&s->s_q[i]);
 	}
@@ -608,6 +609,8 @@ clockpro_tune(void)
 	struct clockpro_state *s = &clockpro;
 	int coldtarget;
 
+	KASSERT(mutex_owned(&s->lock));
+
 #if defined(ADAPTIVE)
 	int coldmax = s->s_npages * CLOCKPRO_COLDPCTMAX / 100;
 	int coldmin = 1;
@@ -639,9 +642,26 @@ clockpro_movereferencebit(struct vm_page
 	kmutex_t *lock;
 	bool referenced;
 
+	KASSERT(mutex_owned(&clockpro.lock));
 	KASSERT(!locked || uvm_page_locked_p(pg));
 	if (!locked) {
+		/*
+		 * acquire interlock to stablize page identity.
+		 * if we have caught the page in a state of flux
+		 * and it should be dequeued, abort.  it will be
+		 * dequeued later.
+		 */
+		mutex_enter(&pg->interlock);
+	        if ((pg->uobject == NULL && pg->uanon == NULL) ||
+	            pg->wire_count > 0) {
+	            	mutex_exit(&pg->interlock);
+			PDPOL_EVCNT_INCR(lockfail);
+			return;
+		}
+		mutex_exit(&clockpro.lock);	/* XXX */
 		lock = uvmpd_trylockowner(pg);
+		/* pg->interlock now dropped */
+		mutex_enter(&clockpro.lock);	/* XXX */
 		if (lock == NULL) {
 			/*
 			 * XXXuvmplock
@@ -664,6 +684,8 @@ static void
 clockpro_clearreferencebit(struct vm_page *pg, bool locked)
 {
 
+	KASSERT(mutex_owned(&clockpro.lock));
+
 	clockpro_movereferencebit(pg, locked);
 	pg->pqflags &= ~PQ_REFERENCED;
 }
@@ -675,6 +697,8 @@ clockpro___newqrotate(int len)
 	pageq_t * const newq = clockpro_queue(s, CLOCKPRO_NEWQ);
 	struct vm_page *pg;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	while (pageq_len(newq) > len) {
 		pg = pageq_remove_head(newq);
 		KASSERT(pg != NULL);
@@ -693,6 +717,8 @@ clockpro_newqrotate(void)
 {
 	struct clockpro_state * const s = &clockpro;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	check_sanity();
 	clockpro___newqrotate(s->s_newqlenmax);
 	check_sanity();
@@ -702,6 +728,8 @@ static void
 clockpro_newqflush(int n)
 {
 
+	KASSERT(mutex_owned(&clockpro.lock));
+
 	check_sanity();
 	clockpro___newqrotate(n);
 	check_sanity();
@@ -712,6 +740,8 @@ clockpro_newqflushone(void)
 {
 	struct clockpro_state * const s = &clockpro;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	clockpro_newqflush(
 	    MAX(pageq_len(clockpro_queue(s, CLOCKPRO_NEWQ)) - 1, 0));
 }
@@ -725,6 +755,7 @@ clockpro___enqueuetail(struct vm_page *p
 {
 	struct clockpro_state * const s = &clockpro;
 
+	KASSERT(mutex_owned(&s->lock));
 	KASSERT(clockpro_getq(pg) == CLOCKPRO_NOQUEUE);
 
 	check_sanity();
@@ -748,7 +779,7 @@ clockpro_pageenqueue(struct vm_page *pg)
 	bool speculative = (pg->pqflags & PQ_SPECULATIVE) != 0; /* XXX */
 
 	KASSERT((~pg->pqflags & (PQ_INITIALREF|PQ_SPECULATIVE)) != 0);
-	KASSERT(mutex_owned(&uvm_pageqlock));
+	KASSERT(mutex_owned(&s->lock));
 	check_sanity();
 	KASSERT(clockpro_getq(pg) == CLOCKPRO_NOQUEUE);
 	s->s_npages++;
@@ -805,6 +836,8 @@ clockpro_pagequeue(struct vm_page *pg)
 	struct clockpro_state * const s = &clockpro;
 	int qidx;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	qidx = clockpro_getq(pg);
 	KASSERT(qidx != CLOCKPRO_NOQUEUE);
 
@@ -817,6 +850,8 @@ clockpro_pagedequeue(struct vm_page *pg)
 	struct clockpro_state * const s = &clockpro;
 	pageq_t *q;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	KASSERT(s->s_npages > 0);
 	check_sanity();
 	q = clockpro_pagequeue(pg);
@@ -838,6 +873,8 @@ clockpro_pagerequeue(struct vm_page *pg)
 	struct clockpro_state * const s = &clockpro;
 	int qidx;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	qidx = clockpro_getq(pg);
 	KASSERT(qidx == CLOCKPRO_HOTQ || qidx == CLOCKPRO_COLDQ);
 	pageq_remove(clockpro_queue(s, qidx), pg);
@@ -851,6 +888,8 @@ static void
 handhot_endtest(struct vm_page *pg)
 {
 
+	KASSERT(mutex_owned(&clockpro.lock));
+
 	KASSERT((pg->pqflags & PQ_HOT) == 0);
 	if ((pg->pqflags & PQ_TEST) != 0) {
 		PDPOL_EVCNT_INCR(hhotcoldtest);
@@ -869,6 +908,8 @@ handhot_advance(void)
 	pageq_t *hotq;
 	int hotqlen;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	clockpro_tune();
 
 	dump("hot called");
@@ -974,6 +1015,8 @@ handcold_advance(void)
 	struct clockpro_state * const s = &clockpro;
 	struct vm_page *pg;
 
+	KASSERT(mutex_owned(&s->lock));
+
 	for (;;) {
 #if defined(LISTQ)
 		pageq_t *listq = clockpro_queue(s, CLOCKPRO_LISTQ);
@@ -1089,7 +1132,9 @@ done:;
 void
 uvmpdpol_pageactivate(struct vm_page *pg)
 {
+	struct clockpro_state * const s = &clockpro;
 
+	mutex_enter(&s->lock);
 	if (!uvmpdpol_pageisqueued_p(pg)) {
 		KASSERT((pg->pqflags & PQ_SPECULATIVE) == 0);
 		pg->pqflags |= PQ_INITIALREF;
@@ -1102,24 +1147,31 @@ uvmpdpol_pageactivate(struct vm_page *pg
 		clockpro_pageenqueue(pg);
 	}
 	pg->pqflags |= PQ_REFERENCED;
+	mutex_exit(&s->lock);
 }
 
 void
 uvmpdpol_pagedeactivate(struct vm_page *pg)
 {
+	struct clockpro_state * const s = &clockpro;
 
+	mutex_enter(&s->lock);
 	clockpro_clearreferencebit(pg, true);
+	mutex_exit(&s->lock);
 }
 
 void
 uvmpdpol_pagedequeue(struct vm_page *pg)
 {
+	struct clockpro_state * const s = &clockpro;
 
 	if (!uvmpdpol_pageisqueued_p(pg)) {
 		return;
 	}
+	mutex_enter(&s->lock);
 	clockpro_pagedequeue(pg);
 	pg->pqflags &= ~(PQ_INITIALREF|PQ_SPECULATIVE);
+	mutex_exit(&s->lock);
 }
 
 void
@@ -1127,12 +1179,16 @@ uvmpdpol_pageenqueue(struct vm_page *pg)
 {
 
 #if 1
+	struct clockpro_state * const s = &clockpro;
+
 	if (uvmpdpol_pageisqueued_p(pg)) {
 		return;
 	}
+	mutex_enter(&s->lock);
 	clockpro_clearreferencebit(pg, true);
 	pg->pqflags |= PQ_SPECULATIVE;
 	clockpro_pageenqueue(pg);
+	mutex_exit(&s->lock);
 #else
 	uvmpdpol_pageactivate(pg);
 #endif
@@ -1141,11 +1197,14 @@ uvmpdpol_pageenqueue(struct vm_page *pg)
 void
 uvmpdpol_anfree(struct vm_anon *an)
 {
+	struct clockpro_state * const s = &clockpro;
 
 	KASSERT(an->an_page == NULL);
+	mutex_enter(&s->lock);
 	if (nonresident_lookupremove((objid_t)an, 0)) {
 		PDPOL_EVCNT_INCR(nresanonfree);
 	}
+	mutex_exit(&s->lock);
 }
 
 void
@@ -1158,8 +1217,11 @@ uvmpdpol_init(void)
 void
 uvmpdpol_reinit(void)
 {
+	struct clockpro_state * const s = &clockpro;
 
+	mutex_enter(&s->lock);
 	clockpro_reinit();
+	mutex_exit(&s->lock);
 }
 
 void
@@ -1167,42 +1229,75 @@ uvmpdpol_estimatepageable(int *active, i
 {
 	struct clockpro_state * const s = &clockpro;
 
+	mutex_enter(&s->lock);
 	if (active) {
 		*active = s->s_npages - s->s_ncold;
 	}
 	if (inactive) {
 		*inactive = s->s_ncold;
 	}
+	mutex_exit(&s->lock);
 }
 
 bool
 uvmpdpol_pageisqueued_p(struct vm_page *pg)
 {
 
+	/* Unlocked check OK due to page lifecycle. */
 	return clockpro_getq(pg) != CLOCKPRO_NOQUEUE;
 }
 
 void
 uvmpdpol_scaninit(void)
 {
+	struct clockpro_state * const s = &clockpro;
 	struct clockpro_scanstate * const ss = &scanstate;
 
+	mutex_enter(&s->lock);
 	ss->ss_nscanned = 0;
+	mutex_exit(&s->lock);
 }
 
 struct vm_page *
-uvmpdpol_selectvictim(void)
+uvmpdpol_selectvictim(kmutex_t **plock)
 {
 	struct clockpro_state * const s = &clockpro;
 	struct clockpro_scanstate * const ss = &scanstate;
 	struct vm_page *pg;
+	kmutex_t *lock = NULL;
 
-	if (ss->ss_nscanned > s->s_npages) {
-		DPRINTF("scan too much\n");
-		return NULL;
-	}
-	pg = handcold_advance();
-	ss->ss_nscanned++;
+	do {
+		mutex_enter(&s->lock);
+		if (ss->ss_nscanned > s->s_npages) {
+			DPRINTF("scan too much\n");
+			mutex_exit(&s->lock);
+			return NULL;
+		}
+		pg = handcold_advance();
+		if (pg == NULL) {
+			mutex_exit(&s->lock);
+			break;
+		}
+		ss->ss_nscanned++;
+		/*
+		 * acquire interlock to stablize page identity.
+		 * if we have caught the page in a state of flux
+		 * and it should be dequeued, do it now and then
+		 * move on to the next.
+		 */
+		mutex_enter(&pg->interlock);
+	        if ((pg->uobject == NULL && pg->uanon == NULL) ||
+	            pg->wire_count > 0) {
+	            	mutex_exit(&pg->interlock);
+			clockpro_pagedequeue(pg);
+			pg->pqflags &= ~(PQ_INITIALREF|PQ_SPECULATIVE);
+	            	continue;
+		}
+		mutex_exit(&s->lock);
+		lock = uvmpd_trylockowner(pg);
+		/* pg->interlock now dropped */
+	} while (lock == NULL);
+	*plock = lock;
 	return pg;
 }
 
@@ -1211,6 +1306,8 @@ clockpro_dropswap(pageq_t *q, int *todo)
 {
 	struct vm_page *pg;
 
+	KASSERT(mutex_owned(&clockpro.lock));
+
 	TAILQ_FOREACH_REVERSE(pg, &q->q_q, pglist, pageq.queue) {
 		if (*todo <= 0) {
 			break;
@@ -1218,12 +1315,15 @@ clockpro_dropswap(pageq_t *q, int *todo)
 		if ((pg->pqflags & PQ_HOT) == 0) {
 			continue;
 		}
-		if ((pg->pqflags & PQ_SWAPBACKED) == 0) {
+		mutex_enter(&pg->interlock);
+		if ((pg->flags & PG_SWAPBACKED) == 0) {
+			mutex_exit(&pg->interlock);
 			continue;
 		}
 		if (uvmpd_trydropswap(pg)) {
 			(*todo)--;
 		}
+		/* pg->interlock now dropped */
 	}
 }
 
@@ -1243,9 +1343,11 @@ uvmpdpol_balancequeue(int swap_shortage)
 
 	DPRINTF("%s: swap_shortage=%d\n", __func__, swap_shortage);
 
+	mutex_enter(&s->lock);
 	clockpro_dropswap(clockpro_queue(s, CLOCKPRO_NEWQ), &todo);
 	clockpro_dropswap(clockpro_queue(s, CLOCKPRO_COLDQ), &todo);
 	clockpro_dropswap(clockpro_queue(s, CLOCKPRO_HOTQ), &todo);
+	mutex_exit(&s->lock);
 
 	DPRINTF("%s: done=%d\n", __func__, swap_shortage - todo);
 }
@@ -1255,17 +1357,18 @@ uvmpdpol_needsscan_p(void)
 {
 	struct clockpro_state * const s = &clockpro;
 
-	if (s->s_ncold < s->s_coldtarget) {
-		return true;
-	}
-	return false;
+	/* This must be an unlocked check: can be called from interrupt. */
+	return s->s_ncold < s->s_coldtarget;
 }
 
 void
 uvmpdpol_tune(void)
 {
+	struct clockpro_state * const s = &clockpro;
 
+	mutex_enter(&s->lock);
 	clockpro_tune();
+	mutex_exit(&s->lock);
 }
 
 #if !defined(PDSIM)

Index: src/sys/uvm/uvm_pglist.c
diff -u src/sys/uvm/uvm_pglist.c:1.72 src/sys/uvm/uvm_pglist.c:1.73
--- src/sys/uvm/uvm_pglist.c:1.72	Tue Nov 13 10:31:01 2018
+++ src/sys/uvm/uvm_pglist.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/*	$NetBSD: uvm_pglist.c,v 1.72 2018/11/13 10:31:01 mrg Exp $	*/
+/*	$NetBSD: uvm_pglist.c,v 1.73 2019/12/13 20:10:22 ad Exp $	*/
 
 /*-
  * Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: uvm_pglist.c,v 1.72 2018/11/13 10:31:01 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: uvm_pglist.c,v 1.73 2019/12/13 20:10:22 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -108,7 +108,6 @@ uvm_pglist_add(struct vm_page *pg, struc
 		uvmexp.zeropages--;
 	VM_FREE_PAGE_TO_CPU(pg)->pages[pgflidx]--;
 	pg->flags = PG_CLEAN;
-	pg->pqflags = 0;
 	pg->uobject = NULL;
 	pg->uanon = NULL;
 	TAILQ_INSERT_TAIL(rlist, pg, pageq.queue);
@@ -574,7 +573,7 @@ uvm_pglistfree(struct pglist *list)
 		KASSERT(!uvmpdpol_pageisqueued_p(pg));
 		TAILQ_REMOVE(list, pg, pageq.queue);
 		iszero = (pg->flags & PG_ZERO);
-		pg->pqflags = PQ_FREE;
+		pg->flags = (pg->flags & PG_ZERO) | PG_FREE;
 #ifdef DEBUG
 		pg->uobject = (void *)0xdeadbeef;
 		pg->uanon = (void *)0xdeadbeef;

Index: src/sys/uvm/uvm_physseg.c
diff -u src/sys/uvm/uvm_physseg.c:1.10 src/sys/uvm/uvm_physseg.c:1.11
--- src/sys/uvm/uvm_physseg.c:1.10	Fri Sep 20 11:09:43 2019
+++ src/sys/uvm/uvm_physseg.c	Fri Dec 13 20:10:22 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: uvm_physseg.c,v 1.10 2019/09/20 11:09:43 maxv Exp $ */
+/* $NetBSD: uvm_physseg.c,v 1.11 2019/12/13 20:10:22 ad Exp $ */
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -1087,6 +1087,7 @@ uvm_physseg_init_seg(uvm_physseg_t upm, 
 	psize_t n;
 	paddr_t paddr;
 	struct uvm_physseg *seg;
+	struct vm_page *pg;
 
 	KASSERT(upm != UVM_PHYSSEG_TYPE_INVALID && pgs != NULL);
 
@@ -1107,10 +1108,11 @@ uvm_physseg_init_seg(uvm_physseg_t upm, 
 		if (atop(paddr) >= seg->avail_start &&
 		    atop(paddr) < seg->avail_end) {
 			uvmexp.npages++;
-			mutex_enter(&uvm_pageqlock);
 			/* add page to free pool */
-			uvm_pagefree(&seg->pgs[i]);
-			mutex_exit(&uvm_pageqlock);
+			pg = &seg->pgs[i];
+			/* Disable LOCKDEBUG: too many and too early. */
+			mutex_init(&pg->interlock, MUTEX_NODEBUG, IPL_NONE);
+			uvm_pagefree(pg);
 		}
 	}
 }

Reply via email to