From: Ingo Molnar

Apparently our thread-creation performance has gone down the tubes again,
because the mm.free_area_cache search heuristic broke.

The initial, more naive hole-cache patch helped the testcode in the
beginning.  Then after some point glibc started creating a 'small hole' in
the vmas, which hole was _below_ the thread stacks, and which hole thus
prevented the intended operation of the cache.

The new code solves the problem by relaxing the 'smallest address hole cache'
rule a bit, the cache is now not re-set at every get_unmapped_area() time,
it's only re-set during unmaps.  It's also re-set if there are no allocatable
mappings at all - ie.  correctness is not affected.



 mm/mmap.c      |   28 -
 mm/mmap.c.orig | 1507 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1525 insertions(+), 10 deletions(-)

diff -puN mm/mmap.c~get_unmapped_area-speedup mm/mmap.c
--- 25/mm/mmap.c~get_unmapped_area-speedup	2003-06-18 23:08:12.000000000 -0700
+++ 25-akpm/mm/mmap.c	2003-06-18 23:08:12.000000000 -0700
@@ -792,7 +792,7 @@ arch_get_unmapped_area(struct file *filp
 {
 	struct mm_struct *mm = current->mm;
 	struct vm_area_struct *vma;
-	int found_hole = 0;
+	unsigned long start_addr;
 
 	if (len > TASK_SIZE)
 		return -ENOMEM;
@@ -804,21 +804,29 @@ arch_get_unmapped_area(struct file *filp
 		    (!vma || addr + len <= vma->vm_start))
 			return addr;
 	}
-	addr = mm->free_area_cache;
+	start_addr = addr = mm->free_area_cache;
 
+full_search:
 	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
 		/* At this point:  (!vma || addr < vma->vm_end). */
-		if (TASK_SIZE - len < addr)
+		if (TASK_SIZE - len < addr) {
+			/*
+			 * Start a new search - just in case we missed
+			 * some holes.
+			 */
+			if (start_addr != TASK_UNMAPPED_BASE) {
+				start_addr = addr = TASK_UNMAPPED_BASE;
+				goto full_search;
+			}
 			return -ENOMEM;
-		/*
-		 * Record the first available hole.
-		 */
-		if (!found_hole && (!vma || addr < vma->vm_start)) {
-			mm->free_area_cache = addr;
-			found_hole = 1;
 		}
-		if (!vma || addr + len <= vma->vm_start)
+		if (!vma || addr + len <= vma->vm_start) {
+			/*
+			 * Remember the place where we stopped the search:
+			 */
+			mm->free_area_cache = addr + len;
 			return addr;
+		}
 		addr = vma->vm_end;
 	}
 }