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 - [[EMAIL PROTECTED]|[EMAIL PROTECTED]]
"I have the heart of a child; I keep it in a jar on my desk."


To Unsubscribe: send mail to [EMAIL PROTECTED]
with "unsubscribe freebsd-hackers" in the body of the message

Reply via email to