DragonFly BSD
DragonFly submit List (threaded) for 2005-02
[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]

Re: improved version - Re: contig memory allocator fix (vm_contig.c)


From: Matthew Dillon <dillon@xxxxxxxxxxxxxxxxxxxx>
Date: Tue, 22 Feb 2005 11:38:29 -0800 (PST)

    I did a cleanup pass on Andrew's code and rearranged a few minor things,
    plus removed the vm_contig_[int]_pg_alloc() shim and just made the
    original vm_contig_pg_alloc() take an mflags argument, and changed
    the mflags type to an int.

    It looks like nothing else in the system used the vm_contig_pg_alloc()
    entry point anyway so I also made that static.

    Please test.

						-Matt

Index: vm/vm_contig.c
===================================================================
RCS file: /cvs/src/sys/vm/vm_contig.c,v
retrieving revision 1.12
diff -u -r1.12 vm_contig.c
--- vm/vm_contig.c	10 Nov 2004 20:19:51 -0000	1.12
+++ vm/vm_contig.c	22 Feb 2005 19:33:20 -0000
@@ -146,8 +146,8 @@
 
 	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,15 +170,29 @@
 				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:
  *
  * Allocate contiguous pages from the VM.  This function does not
@@ -187,15 +201,10 @@
  *
  * 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 @@
 		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 @@
 		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 @@
 	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: vm/vm_page.h
===================================================================
RCS file: /cvs/src/sys/vm/vm_page.h,v
retrieving revision 1.16
diff -u -r1.16 vm_page.h
--- vm/vm_page.h	4 Oct 2004 09:05:26 -0000	1.16
+++ vm/vm_page.h	22 Feb 2005 19:32:17 -0000
@@ -418,7 +418,6 @@
 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);
 



[Date Prev][Date Next]  [Thread Prev][Thread Next]  [Date Index][Thread Index]