Index | Thread | Search

From:
Martin Pieuchot <mpi@grenadille.net>
Subject:
Re: Consolidate building & cleaning swap clusters
To:
bluhm@openbsd.org, sthen@openbsd.org, tb@openbsd.org, tech@openbsd.org
Date:
Wed, 10 Dec 2025 10:03:45 +0100

Download raw body.

Thread
On 05/12/25(Fri) 17:25, Martin Pieuchot wrote:
> Diff below is a cleanup to untangle swapping code from the "pager
> layer".  It also simplifies error handling in the page daemon.  This
> is a necessary step towards a more efficient swapping process.
> 
> It includes:
> 
> - Make the page daemon call uvm_swap_put() directly with the cluster it
>   just built
> 
> - Let it call uvm_swap_free() for transient failure to unobfuscate the
>   code (symmetry with uvm_swap_alloc())
> 
> - Rename uvm_aio_aiodone_pages() into uvm_swap_dropcluster() and
>   consolidate it with the logic from uvm_pager_dropcluster() that
>   applies to swap backed pages.
> 
> - Use uvm_swap_dropcluster() instead of uvm_pager_dropcluster() to clean
>   up clusters built by the page daemon.  Note that with the current page
>   daemon design, pages are never freed at this stage.
> 
> - Simplify uvm_pager_put() and uvm_pager_dropcluster() by removing all
>   swap-related code.  These functions are now only used by vnode-backed
>   pages.
> 
> - Clean all pages related to a given UVM object in uvm_pager_dropcluster().
>   Since the page daemon is using an iterator to select which page needs
>   to be recycled from the inactive LRU there is no real reason to return
>   the last one.
> 
> - Get rid of custom & ugly error cleanings after uvm_pager_put()

Updated diff after recent commits.

Index: uvm/uvm_pager.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_pager.c,v
diff -u -p -r1.95 uvm_pager.c
--- uvm/uvm_pager.c	13 Nov 2025 11:06:13 -0000	1.95
+++ uvm/uvm_pager.c	10 Dec 2025 08:44:54 -0000
@@ -448,22 +448,17 @@ uvm_mk_pcluster(struct uvm_object *uobj,
  * possible.
  *
  * => page queues must be locked by caller
- * => if page is not swap-backed, then "uobj" points to the object
- *	backing it.
- * => if page is swap-backed, then "uobj" should be NULL.
+ * => "uobj" points to the object backing it.
  * => "pg" should be PG_BUSY (by caller), and !PG_CLEAN
- *    for swap-backed memory, "pg" can be NULL if there is no page
- *    of interest [sometimes the case for the pagedaemon]
  * => "ppsp_ptr" should point to an array of npages vm_page pointers
  *	for possible cluster building
- * => flags (first two for non-swap-backed pages)
+ * => flags
  *	PGO_ALLPAGES: all pages in uobj are valid targets
  *	PGO_DOACTCLUST: include "PQ_ACTIVE" pages as valid targets
  *	PGO_SYNCIO: do SYNC I/O (no async)
  *	PGO_PDFREECLUST: pagedaemon: drop cluster on successful I/O
  *	PGO_FREE: tell the aio daemon to free pages in the async case.
- * => start/stop: if (uobj && !PGO_ALLPAGES) limit targets to this range
- *		  if (!uobj) start is the (daddr_t) of the starting swapblk
+ * => start/stop: if !PGO_ALLPAGES limit targets to this range
  * => return state:
  *	1. we return the VM_PAGER status code of the pageout
  *	2. we return with the page queues unlocked
@@ -480,40 +475,24 @@ uvm_pager_put(struct uvm_object *uobj, s
     voff_t start, voff_t stop)
 {
 	int result;
-	daddr_t swblk;
 	struct vm_page **ppsp = *ppsp_ptr;
 
 	/*
-	 * note that uobj is null  if we are doing a swap-backed pageout.
-	 * note that uobj is !null if we are doing normal object pageout.
 	 * note that the page queues must be locked to cluster.
 	 */
-	if (uobj) {	/* if !swap-backed */
-		/*
-		 * attempt to build a cluster for pageout using its
-		 * make-put-cluster function (if it has one).
-		 */
-		if (uobj->pgops->pgo_mk_pcluster) {
-			ppsp = uobj->pgops->pgo_mk_pcluster(uobj, ppsp,
-			    npages, pg, flags, start, stop);
-			*ppsp_ptr = ppsp;  /* update caller's pointer */
-		} else {
-			ppsp[0] = pg;
-			*npages = 1;
-		}
+	KASSERT(uobj != NULL && pg != NULL);
 
-		swblk = 0;		/* XXX: keep gcc happy */
+	/*
+	 * attempt to build a cluster for pageout using its
+	 * make-put-cluster function (if it has one).
+	 */
+	if (uobj->pgops->pgo_mk_pcluster) {
+		ppsp = uobj->pgops->pgo_mk_pcluster(uobj, ppsp,
+		    npages, pg, flags, start, stop);
+		*ppsp_ptr = ppsp;  /* update caller's pointer */
 	} else {
-		/*
-		 * for swap-backed pageout, the caller (the pagedaemon) has
-		 * already built the cluster for us.   the starting swap
-		 * block we are writing to has been passed in as "start."
-		 * "pg" could be NULL if there is no page we are especially
-		 * interested in (in which case the whole cluster gets dropped
-		 * in the event of an error or a sync "done").
-		 */
-		swblk = start;
-		/* ppsp and npages should be ok */
+		ppsp[0] = pg;
+		*npages = 1;
 	}
 
 	/* now that we've clustered we can unlock the page queues */
@@ -523,12 +502,7 @@ uvm_pager_put(struct uvm_object *uobj, s
 	 * now attempt the I/O.   if we have a failure and we are
 	 * clustered, we will drop the cluster and try again.
 	 */
-	if (uobj) {
-		result = uobj->pgops->pgo_put(uobj, ppsp, *npages, flags);
-	} else {
-		/* XXX daddr_t -> int */
-		result = uvm_swap_put(swblk, ppsp, *npages, flags);
-	}
+	result = uobj->pgops->pgo_put(uobj, ppsp, *npages, flags);
 
 	/*
 	 * we have attempted the I/O.
@@ -545,63 +519,18 @@ uvm_pager_put(struct uvm_object *uobj, s
 	if (result == VM_PAGER_PEND || result == VM_PAGER_OK) {
 		if (result == VM_PAGER_OK && (flags & PGO_PDFREECLUST)) {
 			/* drop cluster */
-			if (*npages > 1 || pg == NULL)
-				uvm_pager_dropcluster(uobj, pg, ppsp, npages,
-				    PGO_PDFREECLUST);
+			uvm_pager_dropcluster(uobj, ppsp, npages,
+			    PGO_PDFREECLUST);
 		}
 		return (result);
 	}
 
 	/*
 	 * a pager error occurred (even after dropping the cluster, if there
-	 * was one).  give up! the caller only has one page ("pg")
-	 * to worry about.
+	 * was one).
 	 */
-	if (*npages > 1 || pg == NULL) {
-		uvm_pager_dropcluster(uobj, pg, ppsp, npages, PGO_REALLOCSWAP);
+	uvm_pager_dropcluster(uobj, ppsp, npages, 0);
 
-		/*
-		 * for failed swap-backed pageouts with a "pg",
-		 * we need to reset pg's swslot to either:
-		 * "swblk" (for transient errors, so we can retry),
-		 * or 0 (for hard errors).
-		 */
-		if (uobj == NULL) {
-			if (pg != NULL) {
-				if (pg->pg_flags & PQ_ANON) {
-					rw_enter(pg->uanon->an_lock, RW_WRITE);
-					pg->uanon->an_swslot = 0;
-					rw_exit(pg->uanon->an_lock);
-				} else {
-					rw_enter(pg->uobject->vmobjlock, RW_WRITE);
-					uao_set_swslot(pg->uobject,
-					    pg->offset >> PAGE_SHIFT, 0);
-					rw_exit(pg->uobject->vmobjlock);
-				}
-			}
-			/*
-			 * for transient failures, free all the swslots
-			 */
-			if (result == VM_PAGER_AGAIN) {
-				/* XXX daddr_t -> int */
-				uvm_swap_free(swblk, *npages);
-			} else {
-				/*
-				 * for hard errors on swap-backed pageouts,
-				 * mark the swslots as bad.  note that we do not
-				 * free swslots that we mark bad.
-				 */
-				/* XXX daddr_t -> int */
-				uvm_swap_markbad(swblk, *npages);
-			}
-		}
-	}
-
-	/*
-	 * a pager error occurred (even after dropping the cluster, if there
-	 * was one).    give up!   the caller only has one page ("pg")
-	 * to worry about.
-	 */
 	return result;
 }
 
@@ -610,51 +539,25 @@ uvm_pager_put(struct uvm_object *uobj, s
  * got an error, or, if PGO_PDFREECLUST we are un-busying the
  * cluster pages on behalf of the pagedaemon).
  *
- * => uobj, if non-null, is a non-swap-backed object
+ * => uobj is a non-swap-backed object
  * => page queues are not locked
  * => pg is our page of interest (the one we clustered around, can be null)
  * => ppsp/npages is our current cluster
  * => flags: PGO_PDFREECLUST: pageout was a success: un-busy cluster
  *	pages on behalf of the pagedaemon.
- *           PGO_REALLOCSWAP: drop previously allocated swap slots for 
- *		clustered swap-backed pages (except for "pg" if !NULL)
- *		"swblk" is the start of swap alloc (e.g. for ppsp[0])
- *		[only meaningful if swap-backed (uobj == NULL)]
  */
-
 void
-uvm_pager_dropcluster(struct uvm_object *uobj, struct vm_page *pg,
-    struct vm_page **ppsp, int *npages, int flags)
+uvm_pager_dropcluster(struct uvm_object *uobj, struct vm_page **ppsp,
+    int *npages, int flags)
 {
 	int lcv;
 
-	KASSERT(uobj == NULL || rw_write_held(uobj->vmobjlock));
+	KASSERT(rw_write_held(uobj->vmobjlock));
 
-	/* drop all pages but "pg" */
 	for (lcv = 0 ; lcv < *npages ; lcv++) {
-		/* skip "pg" or empty slot */
-		if (ppsp[lcv] == pg || ppsp[lcv] == NULL)
+		/* skip empty slot */
+		if (ppsp[lcv] == NULL)
 			continue;
-	
-		/*
-		 * Note that PQ_ANON bit can't change as long as we are holding
-		 * the PG_BUSY bit (so there is no need to lock the page
-		 * queues to test it).
-		 */
-		if (!uobj) {
-			if (ppsp[lcv]->pg_flags & PQ_ANON) {
-				rw_enter(ppsp[lcv]->uanon->an_lock, RW_WRITE);
-				if (flags & PGO_REALLOCSWAP)
-					  /* zap swap block */
-					  ppsp[lcv]->uanon->an_swslot = 0;
-			} else {
-				rw_enter(ppsp[lcv]->uobject->vmobjlock,
-				    RW_WRITE);
-				if (flags & PGO_REALLOCSWAP)
-					uao_set_swslot(ppsp[lcv]->uobject,
-					    ppsp[lcv]->offset >> PAGE_SHIFT, 0);
-			}
-		}
 
 		/* did someone want the page while we had it busy-locked? */
 		if (ppsp[lcv]->pg_flags & PG_WANTED) {
@@ -686,14 +589,6 @@ uvm_pager_dropcluster(struct uvm_object 
 			pmap_clear_modify(ppsp[lcv]);
 			atomic_setbits_int(&ppsp[lcv]->pg_flags, PG_CLEAN);
 		}
-
-		/* if anonymous cluster, unlock object and move on */
-		if (!uobj) {
-			if (ppsp[lcv]->pg_flags & PQ_ANON)
-				rw_exit(ppsp[lcv]->uanon->an_lock);
-			else
-				rw_exit(ppsp[lcv]->uobject->vmobjlock);
-		}
 	}
 }
 
@@ -718,39 +613,25 @@ uvm_aio_biodone(struct buf *bp)
 	mtx_leave(&uvm.aiodoned_lock);
 }
 
-void
-uvm_aio_aiodone_pages(struct vm_page **pgs, int npages, boolean_t write,
-    int error)
+/*
+ * uvm_swap_dropcluster: drop a cluster we have built (because we
+ * got an error, or, we are un-busying the cluster pages on behalf
+ * of the pagedaemon.
+ */
+int
+uvm_swap_dropcluster(struct vm_page **pgs, int npages, int error)
 {
 	struct vm_page *pg;
-	struct rwlock *slock;
-	boolean_t swap;
-	int i, swslot;
-
-	slock = NULL;
-	pg = pgs[0];
-	swap = (pg->uanon != NULL && pg->uobject == NULL) ||
-		(pg->pg_flags & PQ_AOBJ) != 0;
-
-	KASSERT(swap);
-	KASSERT(write);
-
-	if (error) {
-		if (pg->uobject != NULL) {
-			swslot = uao_find_swslot(pg->uobject,
-			    pg->offset >> PAGE_SHIFT);
-		} else {
-			swslot = pg->uanon->an_swslot;
-		}
-		KASSERT(swslot);
-	}
+	struct rwlock *slock = NULL;
+	int i, slot = -1;
 
 	for (i = 0; i < npages; i++) {
 		int anon_disposed = 0;
 
 		pg = pgs[i];
 		KASSERT((pg->pg_flags & PG_FAKE) == 0);
-
+		KASSERT((pg->uanon != NULL && pg->uobject == NULL) ||
+			(pg->pg_flags & PQ_AOBJ) != 0);
 		/*
 		 * lock each page's object (or anon) individually since
 		 * each page may need a different lock.
@@ -760,14 +641,37 @@ uvm_aio_aiodone_pages(struct vm_page **p
 		} else {
 			slock = pg->uanon->an_lock;
 		}
+
 		rw_enter(slock, RW_WRITE);
+		if (error) {
+			/* for hard failures return the first slot. */
+			if (error != ENOMEM && i == 0) {
+				if (pg->uobject != NULL) {
+					slot = uao_find_swslot(pg->uobject,
+					    pg->offset >> PAGE_SHIFT);
+				} else {
+					slot = pg->uanon->an_swslot;
+				}
+				KASSERT(slot);
+			}
+			/*
+			 * for failed swap-backed pageouts we need to
+			 * reset pg's swslot to 0.
+			 */
+			if (pg->uobject != NULL)
+				uao_set_swslot(pg->uobject,
+				    pg->offset >> PAGE_SHIFT, 0);
+			else
+				pg->uanon->an_swslot = 0;
+		}
+
 		anon_disposed = (pg->pg_flags & PG_RELEASED) != 0;
 		KASSERT(!anon_disposed || pg->uobject != NULL ||
 		    pg->uanon->an_ref == 0);
 
 		/*
-		 * if this was a successful write,
-		 * mark the page PG_CLEAN.
+		 * if we are operating on behalf of the pagedaemon and
+		 * we had a successful pageout update the page!
 		 */
 		if (!error) {
 			pmap_clear_reference(pg);
@@ -775,20 +679,16 @@ uvm_aio_aiodone_pages(struct vm_page **p
 			atomic_setbits_int(&pg->pg_flags, PG_CLEAN);
 		}
 
-		/*
-		 * unlock everything for this page now.
-		 */
 		if (pg->uobject == NULL && anon_disposed) {
 			uvm_anon_release(pg->uanon);
+			continue;
 		} else {
 			uvm_page_unbusy(&pg, 1);
-			rw_exit(slock);
 		}
+		rw_exit(slock);
 	}
 
-	if (error) {
-		uvm_swap_markbad(swslot, npages);
-	}
+	return slot;
 }
 
 /*
@@ -800,15 +700,13 @@ uvm_aio_aiodone(struct buf *bp)
 {
 	int npages = bp->b_bufsize >> PAGE_SHIFT;
 	struct vm_page *pgs[MAXPHYS >> PAGE_SHIFT];
-	int i, error;
-	boolean_t write;
+	int i, error, slot;
 
 	KASSERT(npages <= MAXPHYS >> PAGE_SHIFT);
+	KASSERT((bp->b_flags & B_READ) == 0);
 	splassert(IPL_BIO);
 
-	error = (bp->b_flags & B_ERROR) ? (bp->b_error ? bp->b_error : EIO) : 0;
-	write = (bp->b_flags & B_READ) == 0;
-
+	error = (bp->b_flags & B_ERROR) ? EIO : 0;
 	for (i = 0; i < npages; i++)
 		pgs[i] = uvm_atopg((vaddr_t)bp->b_data +
 		    ((vsize_t)i << PAGE_SHIFT));
@@ -823,8 +721,15 @@ uvm_aio_aiodone(struct buf *bp)
 	}
 #endif /* UVM_SWAP_ENCRYPT */
 
-	uvm_aio_aiodone_pages(pgs, npages, write, error);
-
+	slot = uvm_swap_dropcluster(pgs, npages, error);
+	/*
+	 * for hard errors on swap-backed pageouts, mark the swslots as
+	 * bad.  note that we do not free swslots that we mark bad.
+	 */
+	if (error) {
+		/* XXX daddr_t -> int */
+		uvm_swap_markbad(slot, npages);
+	}
 #ifdef UVM_SWAP_ENCRYPT
 freed:
 #endif
Index: uvm/uvm_pager.h
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_pager.h,v
diff -u -p -r1.33 uvm_pager.h
--- uvm/uvm_pager.h	12 Oct 2021 07:38:22 -0000	1.33
+++ uvm/uvm_pager.h	10 Dec 2025 08:44:54 -0000
@@ -110,7 +110,6 @@ struct uvm_pagerops {
 #define PGO_DOACTCLUST	0x020	/* flag to mk_pcluster to include active */
 #define PGO_LOCKED	0x040	/* fault data structures are locked [get] */
 #define PGO_PDFREECLUST	0x080	/* daemon's free cluster flag [uvm_pager_put] */
-#define PGO_REALLOCSWAP	0x100	/* reallocate swap area [pager_dropcluster] */
 #define PGO_NOWAIT	0x200	/* do not wait for inode lock */
 
 /* page we are not interested in getting */
@@ -120,8 +119,8 @@ struct uvm_pagerops {
  * prototypes
  */
 
-void		uvm_pager_dropcluster(struct uvm_object *, struct vm_page *,
-		    struct vm_page **, int *, int);
+void		uvm_pager_dropcluster(struct uvm_object *, struct vm_page **,
+		    int *, int);
 void		uvm_pager_init(void);
 int		uvm_pager_put(struct uvm_object *, struct vm_page *, 
 		    struct vm_page ***, int *, int, voff_t, voff_t);
Index: uvm/uvm_pdaemon.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_pdaemon.c,v
diff -u -p -r1.140 uvm_pdaemon.c
--- uvm/uvm_pdaemon.c	10 Dec 2025 08:38:18 -0000	1.140
+++ uvm/uvm_pdaemon.c	10 Dec 2025 08:44:54 -0000
@@ -477,15 +477,14 @@ swapcluster_add(struct swapcluster *swc,
 	return 0;
 }
 
-void
+int
 swapcluster_flush(struct swapcluster *swc)
 {
-	int slot;
-	int nused;
-	int nallocated;
+	int slot, nused, nallocated;
+	int result;
 
 	if (swc->swc_slot == 0)
-		return;
+		return 0; // XXX
 	KASSERT(swc->swc_nused <= swc->swc_nallocated);
 
 	slot = swc->swc_slot;
@@ -494,6 +493,24 @@ swapcluster_flush(struct swapcluster *sw
 
 	if (nused < nallocated)
 		uvm_swap_free(slot + nused, nallocated - nused);
+
+	uvmexp.pdpageouts++;
+	result = uvm_swap_put(slot, swc->swc_pages, nused, 0);
+	if (result != VM_PAGER_PEND) {
+		KASSERT(result == VM_PAGER_AGAIN);
+		uvm_swap_dropcluster(swc->swc_pages, nused, ENOMEM);
+		/*  for transient failures, free all the swslots */
+		/* XXX daddr_t -> int */
+		uvm_swap_free(slot, nused);
+	}
+
+	/*
+	 * zero swslot to indicate that we are
+	 * no longer building a swap-backed cluster.
+	 */
+	swapcluster_init(swc);
+
+	return result;
 }
 
 static inline int
@@ -589,7 +606,6 @@ uvmpd_scan_inactive(struct uvm_pmalloc *
 	struct rwlock *slock;
 	struct vm_anon *anon;
 	boolean_t swap_backed;
-	vaddr_t start;
 	int dirtyreacts;
 
 	/*
@@ -826,151 +846,41 @@ uvmpd_scan_inactive(struct uvm_pmalloc *
 		 * now consider doing the pageout.
 		 *
 		 * for swap-backed pages, we do the pageout if we have either
-		 * filled the cluster (in which case (swnpages == swcpages) or
-		 * run out of pages (p == NULL).
+		 * filled the cluster or run out of pages.
 		 *
 		 * for object pages, we always do the pageout.
 		 */
+		uvmexp.pdpageouts++;
 		if (swap_backed) {
+			uvm_unlock_pageq();
 			/* starting I/O now... set up for it */
 			npages = swc.swc_nused;
-			ppsp = swc.swc_pages;
-			/* for swap-backed pages only */
-			start = (vaddr_t) swc.swc_slot;
-
-			/* if this is final pageout we could have a few
-			 * extra swap blocks */
-			swapcluster_flush(&swc);
+			result = swapcluster_flush(&swc);
 		} else {
 			/* normal object pageout */
 			ppsp = pps;
-			npages = sizeof(pps) / sizeof(struct vm_page *);
-			/* not looked at because PGO_ALLPAGES is set */
-			start = 0;
-		}
+			npages = nitems(pps);
 
-		/*
-		 * now do the pageout.
-		 *
-		 * for swap_backed pages we have already built the cluster.
-		 * for !swap_backed pages, uvm_pager_put will call the object's
-		 * "make put cluster" function to build a cluster on our behalf.
-		 *
-		 * we pass the PGO_PDFREECLUST flag to uvm_pager_put to instruct
-		 * it to free the cluster pages for us on a successful I/O (it
-		 * always does this for un-successful I/O requests).  this
-		 * allows us to do clustered pageout without having to deal
-		 * with cluster pages at this level.
-		 *
-		 * note locking semantics of uvm_pager_put with PGO_PDFREECLUST:
-		 *  IN: locked: page queues
-		 * OUT: locked: 
-		 *     !locked: pageqs
-		 */
-
-		uvmexp.pdpageouts++;
-		result = uvm_pager_put(swap_backed ? NULL : uobj, p,
-		    &ppsp, &npages, PGO_ALLPAGES|PGO_PDFREECLUST, start, 0);
-
-		/*
-		 * if we did i/o to swap, zero swslot to indicate that we are
-		 * no longer building a swap-backed cluster.
-		 */
-		if (swap_backed)
-			swapcluster_init(&swc);	/* done with this cluster */
-
-		/*
-		 * first, we check for VM_PAGER_PEND which means that the
-		 * async I/O is in progress and the async I/O done routine
-		 * will clean up after us.   in this case we move on to the
-		 * next page.
-		 */
-		if (result == VM_PAGER_PEND) {
-			atomic_add_int(&uvmexp.paging, npages);
-			uvm_lock_pageq();
-			uvmexp.pdpending++;
-			continue;
-		}
-
-		/* clean up "p" if we have one */
-		if (p) {
 			/*
-			 * the I/O request to "p" is done and uvm_pager_put
-			 * has freed any cluster pages it may have allocated
-			 * during I/O.  all that is left for us to do is
-			 * clean up page "p" (which is still PG_BUSY).
-			 *
-			 * our result could be one of the following:
-			 *   VM_PAGER_OK: successful pageout
-			 *
-			 *   VM_PAGER_AGAIN: tmp resource shortage, we skip
-			 *     to next page
-			 *   VM_PAGER_{FAIL,ERROR,BAD}: an error.   we
-			 *     "reactivate" page to get it out of the way (it
-			 *     will eventually drift back into the inactive
-			 *     queue for a retry).
-			 *   VM_PAGER_UNLOCK: should never see this as it is
-			 *     only valid for "get" operations
+			 * uvm_pager_put() will call the object's "make put
+			 * cluster" function to build a cluster on our behalf.
+			 * we pass the PGO_PDFREECLUST flag to uvm_pager_put()
+			 * to instruct it to free the cluster pages for us on
+			 * a successful I/O (it always does this for un-
+			 * successful I/O requests).  this allows us to do
+			 * clustered pageout without having to deal with
+			 * cluster pages at this level.
 			 */
-
-			/* relock p's object: page queues not lock yet, so
-			 * no need for "try" */
-
-			/* !swap_backed case: already locked... */
-			if (swap_backed) {
-				rw_enter(slock, RW_WRITE);
-			}
-
-#ifdef DIAGNOSTIC
-			if (result == VM_PAGER_UNLOCK)
-				panic("pagedaemon: pageout returned "
-				    "invalid 'unlock' code");
-#endif
-
-			/* handle PG_WANTED now */
-			if (p->pg_flags & PG_WANTED)
-				wakeup(p);
-
-			atomic_clearbits_int(&p->pg_flags, PG_BUSY|PG_WANTED);
-			UVM_PAGE_OWN(p, NULL);
-
-			/* released during I/O? Can only happen for anons */
-			if (p->pg_flags & PG_RELEASED) {
-				KASSERT(anon != NULL);
-				/*
-				 * remove page so we can get nextpg,
-				 * also zero out anon so we don't use
-				 * it after the free.
-				 */
-				anon->an_page = NULL;
-				p->uanon = NULL;
-
-				uvm_anfree(anon);	/* kills anon */
-				pmap_page_protect(p, PROT_NONE);
-				anon = NULL;
-				/* free released page */
-				uvm_pagefree(p);
-			} else {	/* page was not released during I/O */
-				if (result != VM_PAGER_OK) {
-					/* pageout was a failure... */
-					if (result != VM_PAGER_AGAIN)
-						uvm_pageactivate(p);
-					pmap_clear_reference(p);
-				} else {
-					/* pageout was a success... */
-					pmap_clear_reference(p);
-					pmap_clear_modify(p);
-					atomic_setbits_int(&p->pg_flags,
-					    PG_CLEAN);
-				}
-			}
+			result = uvm_pager_put(uobj, p, &ppsp, &npages,
+			    PGO_ALLPAGES|PGO_PDFREECLUST, 0, 0);
 			rw_exit(slock);
 		}
-		/*
-		 * lock page queues here just so they're always locked
-		 * at the end of the loop.
-		 */
+
 		uvm_lock_pageq();
+		if (result == VM_PAGER_PEND) {
+			atomic_add_int(&uvmexp.paging, npages);
+			uvmexp.pdpending++;
+		}
 	}
 	TAILQ_REMOVE(pglst, &iter, pageq);
 
Index: uvm/uvm_swap.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_swap.c,v
diff -u -p -r1.176 uvm_swap.c
--- uvm/uvm_swap.c	20 Sep 2025 13:53:36 -0000	1.176
+++ uvm/uvm_swap.c	10 Dec 2025 08:44:55 -0000
@@ -1831,8 +1831,7 @@ uvm_swap_io(struct vm_page **pps, int st
 
 		/* dispose of pages we dont use anymore */
 		opages = npages;
-		uvm_pager_dropcluster(NULL, NULL, pps, &opages,
-				      PGO_PDFREECLUST);
+		uvm_swap_dropcluster(pps, opages, 0);
 
 		kva = bouncekva;
 	}
Index: uvm/uvm_swap.h
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_swap.h,v
diff -u -p -r1.20 uvm_swap.h
--- uvm/uvm_swap.h	27 Oct 2023 19:18:53 -0000	1.20
+++ uvm/uvm_swap.h	10 Dec 2025 08:44:55 -0000
@@ -37,6 +37,7 @@
 
 #ifdef _KERNEL
 
+int			uvm_swap_dropcluster(struct vm_page **, int, int);
 int			uvm_swap_get(struct vm_page *, int, int);
 int			uvm_swap_put(int, struct vm_page **, int, int);
 int			uvm_swap_alloc(int *, boolean_t);