Skip to content
  • Nick Piggin's avatar
    mm: vmap area cache · 89699605
    Nick Piggin authored
    Provide a free area cache for the vmalloc virtual address allocator, based
    on the algorithm used by the user virtual memory allocator.
    
    This reduces the number of rbtree operations and linear traversals over
    the vmap extents in order to find a free area, by starting off at the last
    point that a free area was found.
    
    The free area cache is reset if areas are freed behind it, or if we are
    searching for a smaller area or alignment than last time.  So allocation
    patterns are not changed (verified by corner-case and random test cases in
    userspace testing).
    
    This solves a regression caused by lazy vunmap TLB purging introduced in
    db64fe02 (mm: rewrite vmap layer).  That patch will leave extents in the
    vmap allocator after they are vunmapped, and until a significant number
    accumulate that can be flushed in a single batch.  So in a workload that
    vmalloc/vfree frequently, a chain of extents will build up from
    VMALLOC_START address, which have to be iterated over each time (giving an
    O(n) type of behaviour).
    
    After this patch, the search will start from where it left off, giving
    closer to an amortized O(1).
    
    This is verified to solve regressions reported Steven in GFS2, and Avi in
    KVM.
    
    Hugh's update:
    
    : I tried out the recent mmotm, and on one machine was fortunate to hit
    : the BUG_ON(first->va_start < addr) which seems to have been stalling
    : your vmap area cache patch ever since May.
    
    : I can get you addresses etc, I did dump a few out; but once I stared
    : at them, it was easier just to look at the code: and I cannot see how
    : you would be so sure that first->va_start < addr, once you've done
    : that addr = ALIGN(max(...), align) above, if align is over 0x1000
    : (align was 0x8000 or 0x4000 in the cases I hit: ioremaps like Steve).
    
    : I originally got around it by just changing the
    : 		if (first->va_start < addr) {
    : to
    : 		while (first->va_start < addr) {
    : without thinking about it any further; but that seemed unsatisfactory,
    : why would we want to loop here when we've got another very similar
    : loop just below it?
    
    : I am never going to admit how long I've spent trying to grasp your
    : "while (n)" rbtree loop just above this, the one with the peculiar
    : 		if (!first && tmp->va_start < addr + size)
    : in.  That's unfamiliar to me, I'm guessing it's designed to save a
    : subsequent rb_next() in a few circumstances (at risk of then setting
    : a wrong cached_hole_size?); but they did appear few to me, and I didn't
    : feel I could sign off something with that in when I don't grasp it,
    : and it seems responsible for extra code and mistaken BUG_ON below it.
    
    : I've reverted to the familiar rbtree loop that find_vma() does (but
    : with va_end >= addr as you had, to respect the additional guard page):
    : and then (given that cached_hole_size starts out 0) I don't see the
    : need for any complications below it.  If you do want to keep that loop
    : as you had it, please add a comment to explain what it's trying to do,
    : and where addr is relative to first when you emerge from it.
    
    : Aren't your tests "size <= cached_hole_size" and
    : "addr + size > first->va_start" forgetting the guard page we want
    : before the next area?  I've changed those.
    
    : I have not changed your many "addr + size - 1 < addr" overflow tests,
    : but have since come to wonder, shouldn't they be "addr + size < addr"
    : tests - won't the vend checks go wrong if addr + size is 0?
    
    : I have added a few comments - Wolfgang Wander's 2.6.13 description of
    : 1363c3cd
    
     Avoiding mmap fragmentation
    : helped me a lot, perhaps a pointer to that would be good too.  And I found
    : it easier to understand when I renamed cached_start slightly and moved the
    : overflow label down.
    
    : This patch would go after your mm-vmap-area-cache.patch in mmotm.
    : Trivially, nobody is going to get that BUG_ON with this patch, and it
    : appears to work fine on my machines; but I have not given it anything like
    : the testing you did on your original, and may have broken all the
    : performance you were aiming for.  Please take a look and test it out
    : integrate with yours if you're satisfied - thanks.
    
    [akpm@linux-foundation.org: add locking comment]
    Signed-off-by: default avatarNick Piggin <npiggin@suse.de>
    Signed-off-by: default avatarHugh Dickins <hughd@google.com>
    Reviewed-by: default avatarMinchan Kim <minchan.kim@gmail.com>
    Reported-and-tested-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
    Reported-and-tested-by: default avatarAvi Kivity <avi@redhat.com>
    Tested-by: default avatar"Barry J. Marson" <bmarson@redhat.com>
    Cc: Prarit Bhargava <prarit@redhat.com>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    89699605