Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 24 Oct 2000 11:27:08 -0700
From:      Alfred Perlstein <bright@wintelcom.net>
To:        dillon@freebsd.org
Cc:        ps@freebsd.org, hackers@freebsd.org
Subject:   vm_pageout_scan badness
Message-ID:  <20001024112708.E28123@fw.wintelcom.net>

next in thread | raw e-mail | index | archive | help
Matt, I'm not sure if Paul mailed you yet so I thought I'd take the
initiative of bugging you about some reported problems (lockups)
when dealing with machines that have substantial MAP_NOSYNC'd
data along with a page shortage.

What seems to happen is that vm_pageout_scan (src/sys/vm/vm_pageout.c
line 618) keeps rescanning the inactive queue.

My guess is that it just doesn't expect someone to have hosed themselves
by having so many pages that need laundering (maxlaunder/launder_loop)
along with the fact that the comment and code here are doing the wrong
thing for the situation:

	/*
	 * Figure out what to do with dirty pages when they are encountered.
	 * Assume that 1/3 of the pages on the inactive list are clean.  If
	 * we think we can reach our target, disable laundering (do not
	 * clean any dirty pages).  If we miss the target we will loop back
	 * up and do a laundering run.
	 */

	if (cnt.v_inactive_count / 3 > page_shortage) {
		maxlaunder = 0;
		launder_loop = 0;
	} else {
		maxlaunder = 
		    (cnt.v_inactive_target > max_page_launder) ?
		    max_page_launder : cnt.v_inactive_target;
		launder_loop = 1;
	}

The problem is that there's a chance that nearly all the pages on
the inactive queue need laundering and the loop that starts with
the lable 'rescan0:' is never able to clean enough pages before
stumbling across a block that has moved to another queue.  This
forces a jump back to the 'rescan0' lable with effectively nothing
accomplished.

Here's a patch that may help, it's untested, but shows what I'm
hoping to achieve which is force a maximum on the amount of times
rescan0 will be jumped to while not laundering.

Index: vm_pageout.c
===================================================================
RCS file: /home/ncvs/src/sys/vm/vm_pageout.c,v
retrieving revision 1.151.2.4
diff -u -u -r1.151.2.4 vm_pageout.c
--- vm_pageout.c	2000/08/04 22:31:11	1.151.2.4
+++ vm_pageout.c	2000/10/24 07:31:39
@@ -618,7 +618,7 @@
 vm_pageout_scan()
 {
 	vm_page_t m, next;
-	int page_shortage, maxscan, pcount;
+	int page_shortage, maxscan, maxtotscan, pcount;
 	int addl_page_shortage, addl_page_shortage_init;
 	int maxlaunder;
 	int launder_loop = 0;
@@ -672,13 +672,23 @@
 	 * we have scanned the entire inactive queue.
 	 */
 
+rescantot:
+	/* make sure we don't hit rescan0 too many times */
+	maxtotscan = cnt.v_inactive_count;
 rescan0:
 	addl_page_shortage = addl_page_shortage_init;
 	maxscan = cnt.v_inactive_count;
+	if (maxtotscan < 1) {
+		maxlaunder = 
+		    (cnt.v_inactive_target > max_page_launder) ?
+		    max_page_launder : cnt.v_inactive_target;
+	}		
 	for (m = TAILQ_FIRST(&vm_page_queues[PQ_INACTIVE].pl);
 	     m != NULL && maxscan-- > 0 && page_shortage > 0;
 	     m = next) {
 
+		--maxtotscan;
+
 		cnt.v_pdpages++;
 
 		if (m->queue != PQ_INACTIVE) {
@@ -930,7 +940,7 @@
 		maxlaunder = 
 		    (cnt.v_inactive_target > max_page_launder) ?
 		    max_page_launder : cnt.v_inactive_target;
-		goto rescan0;
+		goto rescantot;
 	}
 
 	/*


(still talking about vm_pageout_scan()):

I'm pretty sure that there's yet another problem here, when paging
out a vnode's pages the output routine attempts to cluster them,
this could easily make 'next' point to a page that is cleaned and
put on the FREE queue, when the loop then tests it for
'm->queue != PQ_INACTIVE' it forces 'rescan0' to happen.

I think one could fix this by keeping a pointer to the previous
page then the 'goto rescan0;' test might become something like
this:

	/*
	 * We keep a back reference just in case the vm_pageout_clean()
	 * happens to clean the page after the one we just cleaned
	 * via clustering, this would make next point to something not
	 * one the INACTIVE queue, as a stop-gap we keep a pointer
	 * to the previous page and attempt to use it as a fallback
	 * starting point before actually starting at the head of the
	 * INACTIVE queue again
	 */
	if (m->queue != PQ_INACTIVE) {
		if (prev != NULL && prev->queue == PQ_INACTIVE) {
			m = TAILQ_NEXT(prev, pageq);
			if (m == NULL || m->queue != PQ_INACTIVE)
				goto rescan0;
		} else {
			goto rescan0;
		}
	}


Of course we need to set 'prev' properly, but I need to get back
to my database stuff right now. :)

Also... I wish there was a good hueristic to make max_page_launder
a bit more adaptive, you've done some wonders with bufdaemon so
I'm wondering if you had some ideas about that.

-- 
-Alfred Perlstein - [bright@wintelcom.net|alfred@freebsd.org]
"I have the heart of a child; I keep it in a jar on my desk."


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20001024112708.E28123>