Improve the contigmalloc() memory allocator. Fix a starting index bug,
authorMatthew Dillon <dillon@dragonflybsd.org>
Tue, 22 Feb 2005 21:35:33 +0000 (21:35 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Tue, 22 Feb 2005 21:35:33 +0000 (21:35 +0000)
fix an endless loop bug (do not retry forever), try harder to clean
active pages, and add a third pass.  contigmalloc() is still as nasty as
it has always been but this improves matters considerably.

Submitted-by: Andrew Atrens <atrens@nortel.com>
sys/vm/vm_contig.c
sys/vm/vm_page.h

index 1b8b0b2..439ed3a 100644 (file)
@@ -64,7 +64,7 @@
  * SUCH DAMAGE.
  *
  *     from: @(#)vm_page.c     7.4 (Berkeley) 5/7/91
- * $DragonFly: src/sys/vm/vm_contig.c,v 1.12 2004/11/10 20:19:51 dillon Exp $
+ * $DragonFly: src/sys/vm/vm_contig.c,v 1.13 2005/02/22 21:35:33 dillon Exp $
  */
 
 /*
@@ -146,8 +146,8 @@ vm_contig_pg_clean(int queue)
 
        for (m = TAILQ_FIRST(&vm_page_queues[queue].pl); m != NULL; m = next) {
                KASSERT(m->queue == queue,
-                       ("vm_contig_clean: page %p's queue is not %d", m, queue));
-               
+                       ("vm_contig_clean: page %p's queue is not %d", 
+                       m, queue));
                next = TAILQ_NEXT(m, pageq);
                
                if (vm_page_sleep_busy(m, TRUE, "vpctw0"))
@@ -170,14 +170,28 @@ vm_contig_pg_clean(int queue)
                                return (TRUE);
                        }
                }
-               
                if ((m->dirty == 0) && (m->busy == 0) && (m->hold_count == 0))
                        vm_page_cache(m);
        }
-
        return (FALSE);
 }
 
+/*
+ * vm_contig_pg_flush:
+ * 
+ * Attempt to flush (count) pages from the given page queue.   This may or
+ * may not succeed.  Take up to <count> passes and delay 1/20 of a second
+ * between each pass.
+ */
+static void
+vm_contig_pg_flush(int queue, int count) 
+{
+       while (count > 0) {
+               if (!vm_contig_pg_clean(queue))
+                       break;
+               --count;
+       }
+}
 /*
  * vm_contig_pg_alloc:
  *
@@ -187,15 +201,10 @@ vm_contig_pg_clean(int queue)
  *
  * Malloc()'s data structures have been used for collection of
  * statistics and for allocations of less than a page.
- *
  */
-int
-vm_contig_pg_alloc(
-       unsigned long size,
-       vm_paddr_t low,
-       vm_paddr_t high,
-       unsigned long alignment,
-       unsigned long boundary)
+static int
+vm_contig_pg_alloc(unsigned long size, vm_paddr_t low, vm_paddr_t high,
+       unsigned long alignment, unsigned long boundary, int mflags)
 {
        int i, start, pass;
        vm_offset_t phys;
@@ -212,13 +221,20 @@ vm_contig_pg_alloc(
                panic("vm_contig_pg_alloc: boundary must be a power of 2");
 
        start = 0;
-       for (pass = 0; pass <= 1; pass++) {
-               crit_enter();
-again:
+       crit_enter();
+
+       /*
+        * Three passes (0, 1, 2).  Each pass scans the VM page list for
+        * free or cached pages.  After each pass if the entire scan failed
+        * we attempt to flush inactive pages and reset the start index back
+        * to 0.  For passes 1 and 2 we also attempt to flush active pages.
+        */
+       for (pass = 0; pass < 3; pass++) {
                /*
-                * Find first page in array that is free, within range, aligned, and
-                * such that the boundary won't be crossed.
+                * Find first page in array that is free, within range, 
+                * aligned, and such that the boundary won't be crossed.
                 */
+again:
                for (i = start; i < vmstats.v_page_count; i++) {
                        m = &pga[i];
                        phys = VM_PAGE_TO_PHYS(m);
@@ -244,13 +260,39 @@ again:
                if ((i == vmstats.v_page_count) ||
                        ((VM_PAGE_TO_PHYS(&pga[i]) + size) > high)) {
 
-again1:
-                       if (vm_contig_pg_clean(PQ_INACTIVE))
-                               goto again1;
-                       if (vm_contig_pg_clean(PQ_ACTIVE))
-                               goto again1;
+                       /*
+                        * Best effort flush of all inactive pages.
+                        * This is quite quick, for now stall all
+                        * callers, even if they've specified M_NOWAIT.
+                        */
+                       vm_contig_pg_flush(PQ_INACTIVE, 
+                                           vmstats.v_inactive_count);
+
+                       crit_exit(); /* give interrupts a chance */
+                       crit_enter();
+
+                       /*
+                        * Best effort flush of active pages.
+                        *
+                        * This is very, very slow.
+                        * Only do this if the caller has agreed to M_WAITOK.
+                        *
+                        * If enough pages are flushed, we may succeed on
+                        * next (final) pass, if not the caller, contigmalloc(),
+                        * will fail in the index < 0 case.
+                        */
+                       if (pass > 0 && (mflags & M_WAITOK)) {
+                               vm_contig_pg_flush (PQ_ACTIVE,
+                                                   vmstats.v_active_count);
+                       }
 
-                       crit_exit();
+                       /*
+                        * We're already too high in the address space
+                        * to succeed, reset to 0 for the next iteration.
+                        */
+                       start = 0;
+                       crit_exit(); /* give interrupts a chance */
+                       crit_enter();
                        continue;       /* next pass */
                }
                start = i;
@@ -423,13 +465,13 @@ contigmalloc_map(
        int index;
        void *rv;
 
-       index = vm_contig_pg_alloc(size, low, high, alignment, boundary);
+       index = vm_contig_pg_alloc(size, low, high, alignment, boundary, flags);
        if (index < 0) {
                printf("contigmalloc_map: failed in index < 0 case!");
                return NULL;
        }
 
-       rv = (void *) vm_contig_pg_kmap(index, size, map, flags);
+       rv = (void *)vm_contig_pg_kmap(index, size, map, flags);
        if (!rv)
                vm_contig_pg_free(index, size);
        
index e4e59b3..ad4f28c 100644 (file)
@@ -62,7 +62,7 @@
  * rights to redistribute these changes.
  *
  * $FreeBSD: src/sys/vm/vm_page.h,v 1.75.2.8 2002/03/06 01:07:09 dillon Exp $
- * $DragonFly: src/sys/vm/vm_page.h,v 1.16 2004/10/04 09:05:26 dillon Exp $
+ * $DragonFly: src/sys/vm/vm_page.h,v 1.17 2005/02/22 21:35:33 dillon Exp $
  */
 
 /*
@@ -418,7 +418,6 @@ int vm_page_bits (int, int);
 vm_page_t vm_page_list_find(int basequeue, int index, boolean_t prefer_zero);
 void vm_page_zero_invalid(vm_page_t m, boolean_t setvalid);
 void vm_page_free_toq(vm_page_t m);
-int vm_contig_pg_alloc(u_long, vm_paddr_t, vm_paddr_t, u_long, u_long);
 vm_offset_t vm_contig_pg_kmap(int, u_long, vm_map_t, int);
 void vm_contig_pg_free(int, u_long);