improved version - Re: contig memory allocator fix (vm_contig.c)
Andrew Atrens
atrens at nortelnetworks.com
Mon Feb 21 13:40:04 PST 2005
Improved version - aligns better with caller's M_*WAIT* semantics.
Final version hopefully - looking for feedback :) ...
Andrew.
--- vm_contig.c.old 2004-11-10 15:19:51.000000000 -0500
+++ vm_contig.c 2005-02-21 15:55:48.000000000 -0500
@@ -179,7 +179,22 @@
}
/*
- * vm_contig_pg_alloc:
+ * vm_contig_pg_flush:
+ *
+ * Attempt to flush (count) pages from the given page queue.
+ *
+ * Return TRUE on success
+ */
+static int
+vm_contig_pg_flush( int queue, int count ) {
+ for ( ; count > 0 ; count-- ) {
+ if (! vm_contig_pg_clean( queue ) )
+ break;
+ }
+ return (count > 0);
+}
+/*
+ * vm_contig_int_pg_alloc:
*
* Allocate contiguous pages from the VM. This function does not
* map the allocated pages into the kernel map, otherwise it is
@@ -189,13 +204,14 @@
* statistics and for allocations of less than a page.
*
*/
-int
-vm_contig_pg_alloc(
+static int
+vm_contig_int_pg_alloc(
unsigned long size,
vm_paddr_t low,
vm_paddr_t high,
unsigned long alignment,
- unsigned long boundary)
+ unsigned long boundary,
+ unsigned int mflags )
{
int i, start, pass;
vm_offset_t phys;
@@ -212,8 +228,10 @@
panic("vm_contig_pg_alloc: boundary must be a power of 2");
start = 0;
- for (pass = 0; pass <= 1; pass++) {
- crit_enter();
+
+ crit_enter();
+
+ for (pass = 0; pass <= 2; pass++) {
again:
/*
* Find first page in array that is free, within range, aligned, and
@@ -244,13 +262,40 @@
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.
+ */
+ if ( !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(); /* give interrupts a chance */
- crit_exit();
+ /*
+ * We're already too high in the address space
+ * to succeed, reset to 0 for the next iteration.
+ */
+ start = 0;
+ crit_enter();
continue; /* next pass */
}
start = i;
@@ -311,6 +356,30 @@
return (-1);
}
+
+/*
+ * vm_contig_pg_alloc:
+ *
+ * Allocate contiguous pages from the VM. This function does not
+ * map the allocated pages into the kernel map, otherwise it is
+ * impossible to make large allocations (i.e. >2G).
+ *
+ * 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)
+{
+ return vm_contig_int_pg_alloc(size, low, high,
+ alignment, boundary, M_WAITOK);
+}
+
/*
* vm_contig_pg_free:
*
@@ -423,7 +492,7 @@
int index;
void *rv;
- index = vm_contig_pg_alloc(size, low, high, alignment, boundary);
+ index = vm_contig_int_pg_alloc(size, low, high, alignment, boundary,
flags);
if (index < 0) {
printf("contigmalloc_map: failed in index < 0 case!");
return NULL;
More information about the Submit
mailing list