xmalloc: return unused full pages on multi-page allocations
authorJan Beulich <jbeulich@suse.com>
Thu, 13 Oct 2011 08:03:43 +0000 (10:03 +0200)
committerJan Beulich <jbeulich@suse.com>
Thu, 13 Oct 2011 08:03:43 +0000 (10:03 +0200)
Certain (boot time) allocations are relatively large (particularly when
building with high NR_CPUS), but can also happen to be pretty far away
from a power-of-two size. Utilize the page allocator's (other than
Linux'es) capability of allowing to return space from higher-order
allocations in smaller pieces to return the unused parts immediately.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
xen/common/xmalloc_tlsf.c

index 8a82355e0998b5d4df3858ccc941d752a665e4e6..3235af117c868deaab0909bb484aad0a31732ba3 100644 (file)
@@ -527,13 +527,21 @@ static void xmalloc_pool_put(void *p)
 static void *xmalloc_whole_pages(unsigned long size)
 {
     struct bhdr *b;
-    unsigned int pageorder = get_order_from_bytes(size + BHDR_OVERHEAD);
+    unsigned int i, pageorder = get_order_from_bytes(size + BHDR_OVERHEAD);
+    char *p;
 
     b = alloc_xenheap_pages(pageorder, 0);
     if ( b == NULL )
         return NULL;
 
-    b->size = (1 << (pageorder + PAGE_SHIFT));
+    b->size = PAGE_ALIGN(size + BHDR_OVERHEAD);
+    for ( p = (char *)b + b->size, i = 0; i < pageorder; ++i )
+        if ( (unsigned long)p & (PAGE_SIZE << i) )
+        {
+            free_xenheap_pages(p, i);
+            p += PAGE_SIZE << i;
+        }
+
     return (void *)b->ptr.buffer;
 }
 
@@ -611,7 +619,20 @@ void xfree(void *p)
     }
 
     if ( b->size >= PAGE_SIZE )
-        free_xenheap_pages((void *)b, get_order_from_bytes(b->size));
+    {
+        unsigned int i, order = get_order_from_bytes(b->size);
+
+        BUG_ON((unsigned long)b & ((PAGE_SIZE << order) - 1));
+        for ( i = 0; ; ++i )
+        {
+            if ( !(b->size & (PAGE_SIZE << i)) )
+                continue;
+            b->size -= PAGE_SIZE << i;
+            free_xenheap_pages((void *)b + b->size, i);
+            if ( i + 1 >= order )
+                break;
+        }
+    }
     else
         xmem_pool_free(p, xenpool);
 }