(!vma || addr + len <= vma->vm_start))
                        return addr;
        }
-       start_addr = addr = mm->free_area_cache;
+       if (len > mm->cached_hole_size) {
+               start_addr = addr = mm->free_area_cache;
+       } else {
+               start_addr = addr = TASK_UNMAPPED_BASE;
+               mm->cached_hole_size = 0;
+       }
 
 full_search:
        if (do_align)
                         */
                        if (start_addr != TASK_UNMAPPED_BASE) {
                                start_addr = addr = TASK_UNMAPPED_BASE;
+                               mm->cached_hole_size = 0;
                                goto full_search;
                        }
                        return -ENOMEM;
                        mm->free_area_cache = addr + len;
                        return addr;
                }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
                addr = vma->vm_end;
                if (do_align)
                        addr = COLOUR_ALIGN(addr, pgoff);
 
        struct vm_area_struct *vma;
        unsigned long start_addr;
 
-       start_addr = mm->free_area_cache;
+       if (len > mm->cached_hole_size) {
+               start_addr = mm->free_area_cache;
+       } else {
+               start_addr = TASK_UNMAPPED_BASE;
+               mm->cached_hole_size = 0;
+       }
 
 full_search:
        addr = ALIGN(start_addr, HPAGE_SIZE);
                         */
                        if (start_addr != TASK_UNMAPPED_BASE) {
                                start_addr = TASK_UNMAPPED_BASE;
+                               mm->cached_hole_size = 0;
                                goto full_search;
                        }
                        return -ENOMEM;
                        mm->free_area_cache = addr + len;
                        return addr;
                }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
                addr = ALIGN(vma->vm_end, HPAGE_SIZE);
        }
 }
        struct mm_struct *mm = current->mm;
        struct vm_area_struct *vma, *prev_vma;
        unsigned long base = mm->mmap_base, addr = addr0;
+       unsigned long largest_hole = mm->cached_hole_size;
        int first_time = 1;
 
        /* don't allow allocations above current base */
        if (mm->free_area_cache > base)
                mm->free_area_cache = base;
 
+       if (len <= largest_hole) {
+               largest_hole = 0;
+               mm->free_area_cache  = base;
+       }
 try_again:
        /* make sure it can fit in the remaining address space */
        if (mm->free_area_cache < len)
                 * vma->vm_start, use it:
                 */
                if (addr + len <= vma->vm_start &&
-                               (!prev_vma || (addr >= prev_vma->vm_end)))
+                           (!prev_vma || (addr >= prev_vma->vm_end))) {
                        /* remember the address as a hint for next time */
-                       return (mm->free_area_cache = addr);
-               else
+                       mm->cached_hole_size = largest_hole;
+                       return (mm->free_area_cache = addr);
+               } else {
                        /* pull free_area_cache down to the first hole */
-                       if (mm->free_area_cache == vma->vm_end)
+                       if (mm->free_area_cache == vma->vm_end) {
                                mm->free_area_cache = vma->vm_start;
+                               mm->cached_hole_size = largest_hole;
+                       }
+               }
+
+               /* remember the largest hole we saw so far */
+               if (addr + largest_hole < vma->vm_start)
+                       largest_hole = vma->vm_start - addr;
 
                /* try just below the current vma->vm_start */
                addr = (vma->vm_start - len) & HPAGE_MASK;
         */
        if (first_time) {
                mm->free_area_cache = base;
+               largest_hole = 0;
                first_time = 0;
                goto try_again;
        }
         * allocations.
         */
        mm->free_area_cache = TASK_UNMAPPED_BASE;
+       mm->cached_hole_size = ~0UL;
        addr = hugetlb_get_unmapped_area_bottomup(file, addr0,
                        len, pgoff, flags);
 
         * Restore the topdown base:
         */
        mm->free_area_cache = base;
+       mm->cached_hole_size = ~0UL;
 
        return addr;
 }
 
                    && !is_hugepage_only_range(mm, addr,len))
                        return addr;
        }
-       start_addr = addr = mm->free_area_cache;
+       if (len > mm->cached_hole_size) {
+               start_addr = addr = mm->free_area_cache;
+       } else {
+               start_addr = addr = TASK_UNMAPPED_BASE;
+               mm->cached_hole_size = 0;
+       }
 
 full_search:
        vma = find_vma(mm, addr);
                        mm->free_area_cache = addr + len;
                        return addr;
                }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
                addr = vma->vm_end;
                vma = vma->vm_next;
        }
        /* Make sure we didn't miss any holes */
        if (start_addr != TASK_UNMAPPED_BASE) {
                start_addr = addr = TASK_UNMAPPED_BASE;
+               mm->cached_hole_size = 0;
                goto full_search;
        }
        return -ENOMEM;
        struct vm_area_struct *vma, *prev_vma;
        struct mm_struct *mm = current->mm;
        unsigned long base = mm->mmap_base, addr = addr0;
+       unsigned long largest_hole = mm->cached_hole_size;
        int first_time = 1;
 
        /* requested length too big for entire address space */
                        return addr;
        }
 
+       if (len <= largest_hole) {
+               largest_hole = 0;
+               mm->free_area_cache = base;
+       }
 try_again:
        /* make sure it can fit in the remaining address space */
        if (mm->free_area_cache < len)
                 * vma->vm_start, use it:
                 */
                if (addr+len <= vma->vm_start &&
-                               (!prev_vma || (addr >= prev_vma->vm_end)))
+                         (!prev_vma || (addr >= prev_vma->vm_end))) {
                        /* remember the address as a hint for next time */
-                       return (mm->free_area_cache = addr);
-               else
+                       mm->cached_hole_size = largest_hole;
+                       return (mm->free_area_cache = addr);
+               } else {
                        /* pull free_area_cache down to the first hole */
-                       if (mm->free_area_cache == vma->vm_end)
+                       if (mm->free_area_cache == vma->vm_end) {
                                mm->free_area_cache = vma->vm_start;
+                               mm->cached_hole_size = largest_hole;
+                       }
+               }
+
+               /* remember the largest hole we saw so far */
+               if (addr + largest_hole < vma->vm_start)
+                       largest_hole = vma->vm_start - addr;
 
                /* try just below the current vma->vm_start */
                addr = vma->vm_start-len;
         */
        if (first_time) {
                mm->free_area_cache = base;
+               largest_hole = 0;
                first_time = 0;
                goto try_again;
        }
         * allocations.
         */
        mm->free_area_cache = TASK_UNMAPPED_BASE;
+       mm->cached_hole_size = ~0UL;
        addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
        /*
         * Restore the topdown base:
         */
        mm->free_area_cache = base;
+       mm->cached_hole_size = ~0UL;
 
        return addr;
 }
 
                    (!vma || addr + len <= vma->vm_start))
                        return addr;
        }
+       if (len <= mm->cached_hole_size) {
+               mm->cached_hole_size = 0;
+               mm->free_area_cache = TASK_UNMAPPED_BASE;
+       }
        if (flags & MAP_PRIVATE)
                addr = PAGE_ALIGN(mm->free_area_cache);
        else
                         */
                        if (start_addr != TASK_UNMAPPED_BASE) {
                                start_addr = addr = TASK_UNMAPPED_BASE;
+                               mm->cached_hole_size = 0;
                                goto full_search;
                        }
                        return -ENOMEM;
                        mm->free_area_cache = addr + len;
                        return addr;
                }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
+
                addr = vma->vm_end;
                if (!(flags & MAP_PRIVATE))
                        addr = COLOUR_ALIGN(addr);
 
                        return addr;
        }
 
+       if (len <= mm->cached_hole_size) {
+               mm->cached_hole_size = 0;
+               mm->free_area_cache = TASK_UNMAPPED_BASE;
+       }
        start_addr = addr = mm->free_area_cache;
 
        task_size -= len;
                if (task_size < addr) {
                        if (start_addr != TASK_UNMAPPED_BASE) {
                                start_addr = addr = TASK_UNMAPPED_BASE;
+                               mm->cached_hole_size = 0;
                                goto full_search;
                        }
                        return -ENOMEM;
                        mm->free_area_cache = addr + len;
                        return addr;
                }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
+
                addr = vma->vm_end;
                if (do_color_align)
                        addr = COLOUR_ALIGN(addr, pgoff);
 
        current->mm->brk = ex.a_bss +
                (current->mm->start_brk = N_BSSADDR(ex));
        current->mm->free_area_cache = TASK_UNMAPPED_BASE;
+       current->mm->cached_hole_size = 0;
 
        set_mm_counter(current->mm, rss, 0);
        current->mm->mmap = NULL;
 
                    (!vma || addr + len <= vma->vm_start))
                        return addr;
        }
+       if (((flags & MAP_32BIT) || test_thread_flag(TIF_IA32))
+           && len <= mm->cached_hole_size) {
+               mm->cached_hole_size = 0;
+               mm->free_area_cache = begin;
+       }
        addr = mm->free_area_cache;
        if (addr < begin) 
                addr = begin; 
                         */
                        if (start_addr != begin) {
                                start_addr = addr = begin;
+                               mm->cached_hole_size = 0;
                                goto full_search;
                        }
                        return -ENOMEM;
                        mm->free_area_cache = addr + len;
                        return addr;
                }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
+
                addr = vma->vm_end;
        }
 }
 
        current->mm->brk = ex.a_bss +
                (current->mm->start_brk = N_BSSADDR(ex));
        current->mm->free_area_cache = current->mm->mmap_base;
+       current->mm->cached_hole_size = 0;
 
        set_mm_counter(current->mm, rss, 0);
        current->mm->mmap = NULL;
 
           change some of these later */
        set_mm_counter(current->mm, rss, 0);
        current->mm->free_area_cache = current->mm->mmap_base;
+       current->mm->cached_hole_size = 0;
        retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
                                 executable_stack);
        if (retval < 0) {
 
 
        start_addr = mm->free_area_cache;
 
+       if (len <= mm->cached_hole_size)
+               start_addr = TASK_UNMAPPED_BASE;
+
 full_search:
        addr = ALIGN(start_addr, HPAGE_SIZE);
 
 
 arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
                          unsigned long len, unsigned long pgoff,
                          unsigned long flags);
-extern void arch_unmap_area(struct vm_area_struct *area);
-extern void arch_unmap_area_topdown(struct vm_area_struct *area);
+extern void arch_unmap_area(struct mm_struct *, unsigned long);
+extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long);
 
 #define set_mm_counter(mm, member, value) (mm)->_##member = (value)
 #define get_mm_counter(mm, member) ((mm)->_##member)
        unsigned long (*get_unmapped_area) (struct file *filp,
                                unsigned long addr, unsigned long len,
                                unsigned long pgoff, unsigned long flags);
-       void (*unmap_area) (struct vm_area_struct *area);
-       unsigned long mmap_base;                /* base of mmap area */
-       unsigned long free_area_cache;          /* first hole */
+       void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
+        unsigned long mmap_base;               /* base of mmap area */
+        unsigned long cached_hole_size;         /* if non-zero, the largest hole below free_area_cache */
+       unsigned long free_area_cache;          /* first hole of size cached_hole_size or larger */
        pgd_t * pgd;
        atomic_t mm_users;                      /* How many users with user space? */
        atomic_t mm_count;                      /* How many references to "struct mm_struct" (users count as 1) */
 
        mm->mmap = NULL;
        mm->mmap_cache = NULL;
        mm->free_area_cache = oldmm->mmap_base;
+       mm->cached_hole_size = ~0UL;
        mm->map_count = 0;
        set_mm_counter(mm, rss, 0);
        set_mm_counter(mm, anon_rss, 0);
        mm->ioctx_list = NULL;
        mm->default_kioctx = (struct kioctx)INIT_KIOCTX(mm->default_kioctx, *mm);
        mm->free_area_cache = TASK_UNMAPPED_BASE;
+       mm->cached_hole_size = ~0UL;
 
        if (likely(!mm_alloc_pgd(mm))) {
                mm->def_flags = 0;
 
                    (!vma || addr + len <= vma->vm_start))
                        return addr;
        }
-       start_addr = addr = mm->free_area_cache;
+       if (len > mm->cached_hole_size) {
+               start_addr = addr = mm->free_area_cache;
+       } else {
+               start_addr = addr = TASK_UNMAPPED_BASE;
+               mm->cached_hole_size = 0;
+       }
 
 full_search:
        for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
                         * some holes.
                         */
                        if (start_addr != TASK_UNMAPPED_BASE) {
-                               start_addr = addr = TASK_UNMAPPED_BASE;
+                               addr = TASK_UNMAPPED_BASE;
+                               start_addr = addr;
+                               mm->cached_hole_size = 0;
                                goto full_search;
                        }
                        return -ENOMEM;
                        mm->free_area_cache = addr + len;
                        return addr;
                }
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
                addr = vma->vm_end;
        }
 }
 #endif 
 
-void arch_unmap_area(struct vm_area_struct *area)
+void arch_unmap_area(struct mm_struct *mm, unsigned long addr)
 {
        /*
         * Is this a new hole at the lowest possible address?
         */
-       if (area->vm_start >= TASK_UNMAPPED_BASE &&
-                       area->vm_start < area->vm_mm->free_area_cache)
-               area->vm_mm->free_area_cache = area->vm_start;
+       if (addr >= TASK_UNMAPPED_BASE && addr < mm->free_area_cache) {
+               mm->free_area_cache = addr;
+               mm->cached_hole_size = ~0UL;
+       }
 }
 
 /*
                        return addr;
        }
 
+       /* check if free_area_cache is useful for us */
+       if (len <= mm->cached_hole_size) {
+               mm->cached_hole_size = 0;
+               mm->free_area_cache = mm->mmap_base;
+       }
+
        /* either no address requested or can't fit in requested address hole */
        addr = mm->free_area_cache;
 
                        /* remember the address as a hint for next time */
                        return (mm->free_area_cache = addr);
 
+               /* remember the largest hole we saw so far */
+               if (addr + mm->cached_hole_size < vma->vm_start)
+                       mm->cached_hole_size = vma->vm_start - addr;
+
                /* try just below the current vma->vm_start */
                addr = vma->vm_start-len;
        } while (len < vma->vm_start);
         * can happen with large stack limits and large mmap()
         * allocations.
         */
-       mm->free_area_cache = TASK_UNMAPPED_BASE;
+       mm->cached_hole_size = ~0UL;
+       mm->free_area_cache = TASK_UNMAPPED_BASE;
        addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
        /*
         * Restore the topdown base:
         */
        mm->free_area_cache = mm->mmap_base;
+       mm->cached_hole_size = ~0UL;
 
        return addr;
 }
 #endif
 
-void arch_unmap_area_topdown(struct vm_area_struct *area)
+void arch_unmap_area_topdown(struct mm_struct *mm, unsigned long addr)
 {
        /*
         * Is this a new hole at the highest possible address?
         */
-       if (area->vm_end > area->vm_mm->free_area_cache)
-               area->vm_mm->free_area_cache = area->vm_end;
+       if (addr > mm->free_area_cache)
+               mm->free_area_cache = addr;
 
        /* dont allow allocations above current base */
-       if (area->vm_mm->free_area_cache > area->vm_mm->mmap_base)
-               area->vm_mm->free_area_cache = area->vm_mm->mmap_base;
+       if (mm->free_area_cache > mm->mmap_base)
+               mm->free_area_cache = mm->mmap_base;
 }
 
 unsigned long
        if (area->vm_flags & VM_LOCKED)
                area->vm_mm->locked_vm -= len >> PAGE_SHIFT;
        vm_stat_unaccount(area);
-       area->vm_mm->unmap_area(area);
        remove_vm_struct(area);
 }
 
 {
        struct vm_area_struct **insertion_point;
        struct vm_area_struct *tail_vma = NULL;
+       unsigned long addr;
 
        insertion_point = (prev ? &prev->vm_next : &mm->mmap);
        do {
        } while (vma && vma->vm_start < end);
        *insertion_point = vma;
        tail_vma->vm_next = NULL;
+       if (mm->unmap_area == arch_unmap_area)
+               addr = prev ? prev->vm_end : mm->mmap_base;
+       else
+               addr = vma ?  vma->vm_start : mm->mmap_base;
+       mm->unmap_area(mm, addr);
        mm->mmap_cache = NULL;          /* Kill the cache. */
 }
 
 
        return -ENOMEM;
 }
 
-void arch_unmap_area(struct vm_area_struct *area)
+void arch_unmap_area(struct mm_struct *mm, unsigned long addr)
 {
 }