Subject: Re: patch for handling out-of-swap conditions
To: Bill Sommerfeld <sommerfeld@orchard.arlington.ma.us>
From: Chuck Silvers <chuq@chuq.com>
List: tech-kern
Date: 02/26/1999 08:09:52
I found the problem... I put one of the new bits in the wrong place
when I applied the diff fragments by hand.  I also remembered that
I had an alpha sitting in the corner that would work just fine for
debugging this. I also discovered that most ports don't do the right
thing when uvm_fault returns KERN_RESOURCE_SHORTAGE, so I've put in
the bits for that.

Bill Sommerfeld writes:
> I tried this on a dataless shark with filesystems in NFS, 32meg of
> physical memory and 32meg of swap on a local paging disk; it built ok
> and appears to run ok when not under severe memory pressure.
> 
> However, under stress, it still wedges up.
> 
> The workload was on the order of 15-20 `xterm -sl 10000' processes,
> each running `cat /usr/share/misc/termcap'.
> 
> It quickly got to the point where swap was full, according to pstat
> -s.  Things kept running for for several minutes after that.  However,
> I wasn't able see any process to die due to swap exhaustion; instead,
> things gradually slowed to a crawl to the point that the system might
> as well be hung.  I lost patience and hit the reset button.
> 
> I *suspect* what's going on is that the more-agressive page
> deactivation is preferentially chasing all text pages out of core
> (since they're read-only and backed by the text vnode), replacing them
> with dirty data pages which can't be swapped out because there isn't
> any swap space.  With text pages in really short supply, apps start
> thrashing like crazy.

that right, that's exactly what happens.  the difference that these
patches make is that when *all* of the swap space is used up, the
next (user) page allocation that fails will kill the requesting process.

I set up my test alpha to run diskless with a 1 meg swapfile in nfs,
and my test program was a short thing I wrote that just allocates
anonymous memory with mmap() and modifies all the pages.
it took about 3 seconds to exhaust the miniscule swap area
and then the process was killed.

swapping to nfs has big problems... I got various hangs and panics
due to nfs not being able to keep up.  at first it was working ok
but eventually I had to put a delay in my test program to avoid
the nfs problems.  not sure why these problems didn't show up initially.

anyway, here's the fixed patch.

-Chuck



Index: arch/alpha/alpha/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/alpha/alpha/trap.c,v
retrieving revision 1.40
diff -u -r1.40 trap.c
--- trap.c	1999/02/23 03:20:03	1.40
+++ trap.c	1999/02/26 16:01:28
@@ -468,10 +468,15 @@
 				goto dopanic;
 			}
 			ucode = a0;
-			i = SIGSEGV;
-#ifdef DEBUG
-			printtrap(a0, a1, a2, entry, framep, 1, user);
-#endif
+			if (rv == KERN_RESOURCE_SHORTAGE) {
+				printf("UVM: pid %d (%s), uid %d killed: "
+				       "out of swap\n", p->p_pid, p->p_comm,
+				       p->p_cred && p->p_ucred ?
+				       p->p_ucred->cr_uid : -1);
+				i = SIGKILL;
+			} else {
+				i = SIGSEGV;
+			}
 			break;
 		    }
 
Index: arch/amiga/amiga/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/amiga/amiga/trap.c,v
retrieving revision 1.67
diff -u -r1.67 trap.c
--- trap.c	1998/12/15 19:36:36	1.67
+++ trap.c	1999/02/26 16:01:28
@@ -542,7 +542,14 @@
 		       type, code);
 		panictrap(type, code, v, fp);
 	}
-	trapsignal(p, SIGSEGV, v);
+	if (rv == KERN_RESOURCE_SHORTAGE) {
+		printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+		       p->p_pid, p->p_comm,
+		       p->p_cred && p->p_ucred ? p->p_ucred->cr_uid : -1);
+		trapsignal(p, SIGKILL, v);
+	} else { 
+		trapsignal(p, SIGSEGV, v);
+	}
 	if ((type & T_USER) == 0)
 		return;
 	userret(p, fp->f_pc, sticks); 
Index: arch/arm32/arm32/fault.c
===================================================================
RCS file: /cvsroot/src/sys/arch/arm32/arm32/fault.c,v
retrieving revision 1.36
diff -u -r1.36 fault.c
--- fault.c	1999/02/19 22:32:21	1.36
+++ fault.c	1999/02/26 16:01:28
@@ -455,7 +455,15 @@
 				goto out;
 			
 			report_abort("", fault_status, fault_address, fault_pc);
-			trapsignal(p, SIGSEGV, TRAP_CODE);
+			if (rv = KERN_RESOURCE_SHORTAGE) {
+				printf("UVM: pid %d (%s), uid %d killed: "
+				       "out of swap\n", p->p_pid, p->p_comm,
+				       p->p_cred && p->p_ucred ?
+				       p->p_ucred->cr_uid : -1);
+				trapsignal(p, SIGKILL, TRAP_CODE);
+			} else {
+				trapsignal(p, SIGSEGV, TRAP_CODE);
+			}
 			break;
 		}
 		break;
Index: arch/atari/atari/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/atari/atari/trap.c,v
retrieving revision 1.39
diff -u -r1.39 trap.c
--- trap.c	1998/12/15 19:36:58	1.39
+++ trap.c	1999/02/26 16:01:29
@@ -690,7 +690,15 @@
 			panictrap(type, code, v, &frame);
 		}
 		ucode = v;
-		i = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			i = SIGKILL;
+		} else {
+			i = SIGSEGV;
+		}
 		break;
 	    }
 	}
Index: arch/hp300/hp300/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/hp300/hp300/trap.c,v
retrieving revision 1.68
diff -u -r1.68 trap.c
--- trap.c	1998/12/15 19:37:01	1.68
+++ trap.c	1999/02/26 16:01:30
@@ -704,7 +704,15 @@
 			goto dopanic;
 		}
 		ucode = v;
-		i = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			i = SIGKILL;
+		} else {
+			i = SIGSEGV;
+		}
 		break;
 	    }
 	}
Index: arch/i386/i386/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/trap.c,v
retrieving revision 1.129
diff -u -r1.129 trap.c
--- trap.c	1999/02/13 16:10:44	1.129
+++ trap.c	1999/02/26 16:01:30
@@ -553,8 +553,10 @@
 			goto we_re_toast;
 		}
 		if (rv == KERN_RESOURCE_SHORTAGE) {
-			printf("UVM: process %d killed: out of swap space\n",
-				p->p_pid);
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
 			trapsignal(p, SIGKILL, T_PAGEFLT);
 		} else {
 			trapsignal(p, SIGSEGV, T_PAGEFLT);
Index: arch/mac68k/mac68k/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/mac68k/mac68k/trap.c,v
retrieving revision 1.68
diff -u -r1.68 trap.c
--- trap.c	1998/12/22 08:47:07	1.68
+++ trap.c	1999/02/26 16:01:31
@@ -692,7 +692,15 @@
 			goto dopanic;
 		}
 		ucode = v;
-		i = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			i = SIGKILL;
+		} else {
+			i = SIGSEGV;
+		}
 		break;
 	    }
 	}
Index: arch/mips/mips/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/mips/mips/trap.c,v
retrieving revision 1.102
diff -u -r1.102 trap.c
--- trap.c	1999/01/29 02:18:42	1.102
+++ trap.c	1999/02/26 16:01:32
@@ -591,7 +591,16 @@
 		}
 		if ((type & T_USER) == 0) 
 			goto copyfault;
-		sig = (rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			sig = SIGKILL;
+		} else {
+			sig = (rv == KERN_PROTECTION_FAILURE) ?
+				SIGBUS : SIGSEGV;
+		}
 		ucode = vaddr;
 		break; /* SIGNAL */  
 	    }
Index: arch/mvme68k/mvme68k/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/mvme68k/mvme68k/trap.c,v
retrieving revision 1.27
diff -u -r1.27 trap.c
--- trap.c	1999/02/14 17:54:30	1.27
+++ trap.c	1999/02/26 16:01:32
@@ -697,7 +697,15 @@
 			goto dopanic;
 		}
 		ucode = v;
-		i = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			i = SIGKILL;
+		} else {
+			i = SIGSEGV;
+		}
 		break;
 	    }
 	}
Index: arch/next68k/next68k/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/next68k/next68k/trap.c,v
retrieving revision 1.10
diff -u -r1.10 trap.c
--- trap.c	1999/01/13 09:26:00	1.10
+++ trap.c	1999/02/26 16:01:32
@@ -641,7 +641,15 @@
 			goto dopanic;
 		}
 		ucode = v;
-		i = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			i = SIGKILL;
+		} else {
+			i = SIGSEGV;
+		}
 		break;
 	    }
 	}
Index: arch/pc532/pc532/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/pc532/pc532/trap.c,v
retrieving revision 1.33
diff -u -r1.33 trap.c
--- trap.c	1998/11/11 06:43:50	1.33
+++ trap.c	1999/02/26 16:01:34
@@ -486,8 +486,10 @@
 			goto we_re_toast;
 		}
 		if (rv == KERN_RESOURCE_SHORTAGE) {
-			printf("UVM: process %d killed: out of swap space\n",
-				p->p_pid);
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
 			trapsignal(p, SIGKILL, T_ABT);
 		} else {
 			trapsignal(p, SIGSEGV, T_ABT);
Index: arch/pica/pica/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/pica/pica/trap.c,v
retrieving revision 1.14
diff -u -r1.14 trap.c
--- trap.c	1998/07/05 06:49:08	1.14
+++ trap.c	1999/02/26 16:01:34
@@ -558,7 +558,15 @@
 			goto err;
 		}
 		ucode = vadr;
-		i = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			i = SIGKILL;
+		} else {
+			i = SIGSEGV;
+		}
 		break;
 	    }
 
Index: arch/powerpc/powerpc/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/powerpc/powerpc/trap.c,v
retrieving revision 1.15
diff -u -r1.15 trap.c
--- trap.c	1999/01/10 10:24:17	1.15
+++ trap.c	1999/02/26 16:01:34
@@ -145,7 +145,15 @@
 				break;
 #endif
 		}
-		trapsignal(p, SIGSEGV, EXC_DSI);
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			trapsignal(p, SIGKILL, EXC_DSI);
+		} else {
+			trapsignal(p, SIGSEGV, EXC_DSI);
+		}
 		break;
 	case EXC_ISI|EXC_USER:
 		{
Index: arch/sparc/sparc/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc/sparc/trap.c,v
retrieving revision 1.81
diff -u -r1.81 trap.c
--- trap.c	1999/02/23 06:47:05	1.81
+++ trap.c	1999/02/26 16:01:34
@@ -847,8 +847,10 @@
 			return;
 		}
 		if (rv == KERN_RESOURCE_SHORTAGE) {
-			printf("UVM: process %d killed: out of swap space\n",
-			    p->p_pid);
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
 			trapsignal(p, SIGKILL, (u_int)v);
 		} else
 			trapsignal(p, SIGSEGV, (u_int)v);
Index: arch/sparc64/sparc64/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sparc64/sparc64/trap.c,v
retrieving revision 1.22
diff -u -r1.22 trap.c
--- trap.c	1999/01/31 09:21:19	1.22
+++ trap.c	1999/02/26 16:01:44
@@ -1146,7 +1146,15 @@
 			Debugger();
 		}
 #endif
-		trapsignal(p, SIGSEGV, (u_long)addr);
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			trapsignal(p, SIGKILL, (u_long)addr);
+		} else {
+			trapsignal(p, SIGSEGV, (u_long)addr);
+		}
 	}
 	if ((tstate & TSTATE_PRIV) == 0) {
 		userret(p, pc, sticks);
Index: arch/sun3/sun3/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sun3/sun3/trap.c,v
retrieving revision 1.83
diff -u -r1.83 trap.c
--- trap.c	1998/12/15 19:37:12	1.83
+++ trap.c	1999/02/26 16:01:44
@@ -575,7 +575,15 @@
 			goto dopanic;
 		}
 		ucode = v;
-		sig = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			sig = SIGKILL;
+		} else {
+			sig = SIGSEGV;
+		}
 		break;
 		} /* T_MMUFLT */
 	} /* switch */
Index: arch/vax/vax/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/vax/vax/trap.c,v
retrieving revision 1.41
diff -u -r1.41 trap.c
--- trap.c	1999/01/19 21:04:49	1.41
+++ trap.c	1999/02/26 16:01:44
@@ -226,10 +226,16 @@
 				panic("Segv in kernel mode: pc %x addr %x",
 				    (u_int)frame->pc, (u_int)frame->code);
 			}
-ufault:			if (rv == KERN_RESOURCE_SHORTAGE)
-				printf("Pid %d killed: out of memory.\n",
-				 p->p_pid);
-			sig = SIGSEGV;
+ufault:			if (rv == KERN_RESOURCE_SHORTAGE) {
+				printf("UVM: pid %d (%s), uid %d killed: "
+				       "out of swap\n",
+				       p->p_pid, p->p_comm,
+				       p->p_cred && p->p_ucred ?
+				       p->p_ucred->cr_uid : -1);
+				sig = SIGKILL;
+			} else {
+				sig = SIGSEGV;
+			}
 		} else
 			trapsig = 0;
 		break;
Index: arch/x68k/x68k/trap.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x68k/x68k/trap.c,v
retrieving revision 1.26
diff -u -r1.26 trap.c
--- trap.c	1998/12/15 19:37:17	1.26
+++ trap.c	1999/02/26 16:01:44
@@ -727,7 +727,15 @@
 			goto dopanic;
 		}
 		ucode = v;
-		i = SIGSEGV;
+		if (rv == KERN_RESOURCE_SHORTAGE) {
+			printf("UVM: pid %d (%s), uid %d killed: out of swap\n",
+			       p->p_pid, p->p_comm,
+			       p->p_cred && p->p_ucred ?
+			       p->p_ucred->cr_uid : -1);
+			i = SIGKILL;
+		} else {
+			i = SIGSEGV;
+		}
 		break;
 	    }
 	}
Index: uvm/uvm_anon.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_anon.c,v
retrieving revision 1.1
diff -u -r1.1 uvm_anon.c
--- uvm_anon.c	1999/01/24 23:53:15	1.1
+++ uvm_anon.c	1999/02/26 16:01:48
@@ -235,6 +235,11 @@
 		    anon, anon->an_swslot, 0, 0);
 		uvm_swap_free(anon->an_swslot, 1);
 		anon->an_swslot = 0;
+
+		if (pg == NULL) {
+			/* this page is no longer only in swap. */
+			uvmexp.swpguniq--;
+		}
 	} 
 
 	/*
Index: uvm/uvm_aobj.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_aobj.c,v
retrieving revision 1.15
diff -u -r1.15 uvm_aobj.c
--- uvm_aobj.c	1998/10/18 23:49:59	1.15
+++ uvm_aobj.c	1999/02/26 16:01:48
@@ -428,8 +428,15 @@
 				{
 					int slot = elt->slots[j];
 
-					if (slot)
+					if (slot) {
 						uvm_swap_free(slot, 1);
+
+						/*
+						 * this page is no longer
+						 * only in swap.
+						 */
+						uvmexp.swpguniq--;
+					}
 				}
 
 				next = elt->list.le_next;
@@ -448,8 +455,12 @@
 		{
 			int slot = aobj->u_swslots[i];
 
-			if (slot)
+			if (slot) {
 				uvm_swap_free(slot, 1);
+
+				/* this page is no longer only in swap. */
+				uvmexp.swpguniq--;
+			}
 		}
 		FREE(aobj->u_swslots, M_UVMAOBJ);
 	}
Index: uvm/uvm_extern.h
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_extern.h,v
retrieving revision 1.21
diff -u -r1.21 uvm_extern.h
--- uvm_extern.h	1998/09/08 23:44:21	1.21
+++ uvm_extern.h	1999/02/26 16:01:48
@@ -181,6 +181,7 @@
 	int nswapdev;	/* number of configured swap devices in system */
 	int swpages;	/* number of PAGE_SIZE'ed swap pages */
 	int swpginuse;	/* number of swap pages in use */
+	int swpguniq;	/* number of swap pages in use, not also in RAM */
 	int nswget;	/* number of times fault calls uvm_swap_get() */
 	int nanon;	/* number total of anon's in system */
 	int nfreeanon;	/* number of free anon's */
Index: uvm/uvm_fault.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_fault.c,v
retrieving revision 1.20
diff -u -r1.20 uvm_fault.c
--- uvm_fault.c	1999/01/31 09:27:18	1.20
+++ uvm_fault.c	1999/02/26 16:01:48
@@ -1150,13 +1150,15 @@
 			if (anon)
 				uvm_anfree(anon);
 			uvmfault_unlockall(&ufi, amap, uobj, oanon);
-			if (anon == NULL) {
+
+			if (anon == NULL || uvmexp.swpages == uvmexp.swpguniq) {
 				UVMHIST_LOG(maphist,
 				    "<- failed.  out of VM",0,0,0,0);
 				uvmexp.fltnoanon++;
 				/* XXX: OUT OF VM, ??? */
 				return (KERN_RESOURCE_SHORTAGE);
 			}
+
 			uvmexp.fltnoram++;
 			uvm_wait("flt_noram3");	/* out of RAM, wait for more */
 			goto ReFault;
@@ -1542,13 +1544,15 @@
 
 			/* unlock and fail ... */
 			uvmfault_unlockall(&ufi, amap, uobj, NULL);
-			if (anon == NULL) {
+
+			if (anon == NULL || uvmexp.swpages == uvmexp.swpguniq) {
 				UVMHIST_LOG(maphist, "  promote: out of VM",
 				    0,0,0,0);
 				uvmexp.fltnoanon++;
 				/* XXX: out of VM */
 				return (KERN_RESOURCE_SHORTAGE);
 			}
+
 			UVMHIST_LOG(maphist, "  out of RAM, waiting for more",
 			    0,0,0,0);
 			uvm_anfree(anon);
Index: uvm/uvm_pdaemon.c
===================================================================
RCS file: /cvsroot/src/sys/uvm/uvm_pdaemon.c,v
retrieving revision 1.12
diff -u -r1.12 uvm_pdaemon.c
--- uvm_pdaemon.c	1998/11/04 07:06:05	1.12
+++ uvm_pdaemon.c	1999/02/26 16:01:49
@@ -481,6 +481,11 @@
 			 */
 
 			if (p->flags & PG_CLEAN) {
+				if (p->pqflags & PQ_SWAPBACKED) {
+					/* this page now lives only in swap */
+					uvmexp.swpguniq++;
+				}
+
 				/* zap all mappings with pmap_page_protect... */
 				pmap_page_protect(PMAP_PGARG(p), VM_PROT_NONE);
 				uvm_pagefree(p);
@@ -880,7 +885,7 @@
 void
 uvmpd_scan()
 {
-	int s, free, pages_freed, page_shortage;
+	int s, free, inactive_shortage, swap_shortage;
 	struct vm_page *p, *nextpg;
 	struct uvm_object *uobj;
 	boolean_t got_it;
@@ -926,7 +931,6 @@
 	 */
 
 	UVMHIST_LOG(pdhist, "  starting 'free' loop",0,0,0,0);
-	pages_freed = uvmexp.pdfreed;	/* so far... */
 
 	/*
 	 * do loop #1!   alternate starting queue between swap and object based
@@ -943,24 +947,43 @@
 
 	/*
 	 * we have done the scan to get free pages.   now we work on meeting
-	 * our inactive target.
+	 * our inactive target.  if we are still below the free target
+	 * and we didn't start any pageouts in the inactive scan above
+	 * (perhaps because we're out of swap space) and we've met
+	 * the inactive target, then go ahead and deactivate some more
+	 * pages anyway.  meeting the free target is important enough
+	 * that it's worth temporarily reducing the number of active pages.
 	 */
+
+	inactive_shortage = uvmexp.inactarg - uvmexp.inactive;
+	if (free < uvmexp.freetarg && inactive_shortage <= 0 &&
+	    uvmexp.paging == 0) {
+		inactive_shortage = 16;
+	}
 
-	page_shortage = uvmexp.inactarg - uvmexp.inactive;
-	pages_freed = uvmexp.pdfreed - pages_freed; /* # pages freed in loop */
-	if (page_shortage <= 0 && pages_freed == 0)
-		page_shortage = 1;
-
-	UVMHIST_LOG(pdhist, "  second loop: page_shortage=%d", page_shortage,
-	    0, 0, 0);
-	for (p = uvm.page_active.tqh_first ; 
-	    p != NULL && page_shortage > 0 ; p = nextpg) {
+	/*
+	 * detect if we're not going to be able to page anything out
+	 * until we free some swap resources from active pages.
+	 */
+	swap_shortage = 0;
+	if (uvmexp.free < uvmexp.freetarg &&
+	    uvmexp.swpginuse == uvmexp.swpages &&
+	    uvmexp.swpguniq < uvmexp.swpages &&
+	    uvmexp.paging == 0) {
+		swap_shortage = uvmexp.freetarg - uvmexp.free;
+	}
+ 
+	UVMHIST_LOG(pdhist, "  loop 2: inactive_shortage=%d swap_shortage=%d",
+		    inactive_shortage, swap_shortage,0,0);
+	for (p = TAILQ_FIRST(&uvm.page_active); 
+	     p != NULL && (inactive_shortage > 0 || swap_shortage > 0);
+	     p = nextpg) {
 		nextpg = p->pageq.tqe_next;
 		if (p->flags & PG_BUSY)
 			continue;	/* quick check before trying to lock */
 
 		/*
-		 * lock owner
+		 * lock the page's owner.
 		 */
 		/* is page anon owned or ownerless? */
 		if ((p->pqflags & PQ_ANON) || p->uobject == NULL) {
@@ -970,36 +993,66 @@
 				panic("pagedaemon: page with no anon or "
 				    "object detected - loop 2");
 #endif
-
 			if (!simple_lock_try(&p->uanon->an_lock))
 				continue;
 
 			/* take over the page? */
 			if ((p->pqflags & PQ_ANON) == 0) {
-
 #ifdef DIAGNOSTIC
 				if (p->loan_count < 1)
 					panic("pagedaemon: non-loaned "
 					    "ownerless page detected - loop 2");
 #endif
-
 				p->loan_count--;
 				p->pqflags |= PQ_ANON;
 			}
-
 		} else {
-
 			if (!simple_lock_try(&p->uobject->vmobjlock))
 				continue;
-
 		}
-
-		if ((p->flags & PG_BUSY) == 0) {
+		/*
+		 * skip this page if it's busy.
+		 */
+		if ((p->flags & PG_BUSY) != 0) {
+			if (p->pqflags & PQ_ANON)
+				simple_unlock(&p->uanon->an_lock);
+			else
+				simple_unlock(&p->uobject->vmobjlock);
+			continue;
+		}
+ 
+		/*
+		 * free any swap allocated to this page
+		 * if there's a shortage of swap.
+		 */
+		if (swap_shortage > 0) {
+			if (p->pqflags & PQ_ANON && p->uanon->an_swslot) {
+				uvm_swap_free(p->uanon->an_swslot, 1);
+				p->uanon->an_swslot = 0;
+				p->flags &= ~PG_CLEAN;
+				swap_shortage--;
+			}
+			if (p->pqflags & PQ_AOBJ) {
+				int slot = uao_set_swslot(p->uobject,
+					p->offset >> PAGE_SHIFT, 0);
+				if (slot) {
+					uvm_swap_free(slot, 1);
+					p->flags &= ~PG_CLEAN;
+					swap_shortage--;
+				}
+			}
+		}
+ 
+		/*
+		 * deactivate this page if there's a shortage of
+		 * inactive pages.
+		 */
+		if (inactive_shortage > 0) {
 			pmap_page_protect(PMAP_PGARG(p), VM_PROT_NONE);
 			/* no need to check wire_count as pg is "active" */
 			uvm_pagedeactivate(p);
 			uvmexp.pddeact++;
-			page_shortage--;
+			inactive_shortage--;
 		}
 
 		if (p->pqflags & PQ_ANON)
@@ -1007,8 +1060,4 @@
 		else
 			simple_unlock(&p->uobject->vmobjlock);
 	}
-
-	/*
-	 * done scan
-	 */
 }