From owner-freebsd-hackers Tue Oct 24 11:27:14 2000 Delivered-To: freebsd-hackers@freebsd.org Received: from fw.wintelcom.net (ns1.wintelcom.net [209.1.153.20]) by hub.freebsd.org (Postfix) with ESMTP id D137E37B4C5; Tue, 24 Oct 2000 11:27:08 -0700 (PDT) Received: (from bright@localhost) by fw.wintelcom.net (8.10.0/8.10.0) id e9OIR8313379; Tue, 24 Oct 2000 11:27:08 -0700 (PDT) Date: Tue, 24 Oct 2000 11:27:08 -0700 From: Alfred Perlstein To: dillon@freebsd.org Cc: ps@freebsd.org, hackers@freebsd.org Subject: vm_pageout_scan badness Message-ID: <20001024112708.E28123@fw.wintelcom.net> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.2.4i Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG 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