Subject: patch for handling out-of-swap conditions
To: None <tech-kern@netbsd.org>
From: Chuck Silvers <chuq@chuq.com>
List: tech-kern
Date: 02/25/1999 09:29:33
could folks try out this patch for handling of out-of-swap conditions?
it's separated out from the UBC branch, but I'm not sure I got all the
pieces.  I'd test it myself, but my test machine is having a strange
crashing problem with -current and I don't have time to figure that out
right now.  I'd like to get this into 1.4, thus this request.

the difference that this patch should make is that when swap is exhausted,
a process that's trying to allocate anonymous memory will be killed,
rather than the system just wedging up like it does now.
please let me know whether or not this patch works for you.

thanks,
-Chuck



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/23 16:13:11
@@ -237,6 +237,11 @@
 		anon->an_swslot = 0;
 	} 
 
+	if (pg == NULL) {
+		/* this page is no longer only in swap. */
+		uvmexp.swpguniq--;
+	}
+
 	/*
 	 * now that we've stripped the data areas from the anon, free the anon
 	 * itself!
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/23 16:13:13
@@ -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/23 16:13:13
@@ -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/23 16:13:18
@@ -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/23 16:13:18
@@ -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
-	 */
 }