Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[pandora-kernel.git] / arch / powerpc / lib / rheap.c
index 6c5c5dd..22c3b4f 100644 (file)
@@ -15,7 +15,9 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/mm.h>
+#include <linux/err.h>
 #include <linux/slab.h>
 
 #include <asm/rheap.h>
@@ -133,7 +135,7 @@ static rh_block_t *get_slot(rh_info_t * info)
        info->empty_slots--;
 
        /* Initialize */
-       blk->start = NULL;
+       blk->start = 0;
        blk->size = 0;
        blk->owner = NULL;
 
@@ -158,7 +160,7 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
 
        /* We assume that they are aligned properly */
        size = blkn->size;
-       s = (unsigned long)blkn->start;
+       s = blkn->start;
        e = s + size;
 
        /* Find the blocks immediately before and after the given one
@@ -170,7 +172,7 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
        list_for_each(l, &info->free_list) {
                blk = list_entry(l, rh_block_t, list);
 
-               bs = (unsigned long)blk->start;
+               bs = blk->start;
                be = bs + blk->size;
 
                if (next == NULL && s >= bs)
@@ -188,10 +190,10 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
        }
 
        /* Now check if they are really adjacent */
-       if (before != NULL && s != (unsigned long)before->start + before->size)
+       if (before && s != (before->start + before->size))
                before = NULL;
 
-       if (after != NULL && e != (unsigned long)after->start)
+       if (after && e != after->start)
                after = NULL;
 
        /* No coalescing; list insert and return */
@@ -216,7 +218,7 @@ static void attach_free_block(rh_info_t * info, rh_block_t * blkn)
 
        /* Grow the after block backwards */
        if (before == NULL && after != NULL) {
-               after->start = (int8_t *)after->start - size;
+               after->start -= size;
                after->size += size;
                return;
        }
@@ -274,6 +276,7 @@ rh_info_t *rh_create(unsigned int alignment)
 
        return info;
 }
+EXPORT_SYMBOL_GPL(rh_create);
 
 /*
  * Destroy a dynamically created remote heap.  Deallocate only if the areas
@@ -287,6 +290,7 @@ void rh_destroy(rh_info_t * info)
        if ((info->flags & RHIF_STATIC_INFO) == 0)
                kfree(info);
 }
+EXPORT_SYMBOL_GPL(rh_destroy);
 
 /*
  * Initialize in place a remote heap info block.  This is needed to support
@@ -319,16 +323,17 @@ void rh_init(rh_info_t * info, unsigned int alignment, int max_blocks,
        for (i = 0, blk = block; i < max_blocks; i++, blk++)
                list_add(&blk->list, &info->empty_list);
 }
+EXPORT_SYMBOL_GPL(rh_init);
 
 /* Attach a free memory region, coalesces regions if adjuscent */
-int rh_attach_region(rh_info_t * info, void *start, int size)
+int rh_attach_region(rh_info_t * info, unsigned long start, int size)
 {
        rh_block_t *blk;
        unsigned long s, e, m;
        int r;
 
        /* The region must be aligned */
-       s = (unsigned long)start;
+       s = start;
        e = s + size;
        m = info->alignment - 1;
 
@@ -338,9 +343,12 @@ int rh_attach_region(rh_info_t * info, void *start, int size)
        /* Round end down */
        e = e & ~m;
 
+       if (IS_ERR_VALUE(e) || (e < s))
+               return -ERANGE;
+
        /* Take final values */
-       start = (void *)s;
-       size = (int)(e - s);
+       start = s;
+       size = e - s;
 
        /* Grow the blocks, if needed */
        r = assure_empty(info, 1);
@@ -356,9 +364,10 @@ int rh_attach_region(rh_info_t * info, void *start, int size)
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(rh_attach_region);
 
 /* Detatch given address range, splits free block if needed. */
-void *rh_detach_region(rh_info_t * info, void *start, int size)
+unsigned long rh_detach_region(rh_info_t * info, unsigned long start, int size)
 {
        struct list_head *l;
        rh_block_t *blk, *newblk;
@@ -366,10 +375,10 @@ void *rh_detach_region(rh_info_t * info, void *start, int size)
 
        /* Validate size */
        if (size <= 0)
-               return ERR_PTR(-EINVAL);
+               return (unsigned long) -EINVAL;
 
        /* The region must be aligned */
-       s = (unsigned long)start;
+       s = start;
        e = s + size;
        m = info->alignment - 1;
 
@@ -380,34 +389,34 @@ void *rh_detach_region(rh_info_t * info, void *start, int size)
        e = e & ~m;
 
        if (assure_empty(info, 1) < 0)
-               return ERR_PTR(-ENOMEM);
+               return (unsigned long) -ENOMEM;
 
        blk = NULL;
        list_for_each(l, &info->free_list) {
                blk = list_entry(l, rh_block_t, list);
                /* The range must lie entirely inside one free block */
-               bs = (unsigned long)blk->start;
-               be = (unsigned long)blk->start + blk->size;
+               bs = blk->start;
+               be = blk->start + blk->size;
                if (s >= bs && e <= be)
                        break;
                blk = NULL;
        }
 
        if (blk == NULL)
-               return ERR_PTR(-ENOMEM);
+               return (unsigned long) -ENOMEM;
 
        /* Perfect fit */
        if (bs == s && be == e) {
                /* Delete from free list, release slot */
                list_del(&blk->list);
                release_slot(info, blk);
-               return (void *)s;
+               return s;
        }
 
        /* blk still in free list, with updated start and/or size */
        if (bs == s || be == e) {
                if (bs == s)
-                       blk->start = (int8_t *)blk->start + size;
+                       blk->start += size;
                blk->size -= size;
 
        } else {
@@ -416,89 +425,106 @@ void *rh_detach_region(rh_info_t * info, void *start, int size)
 
                /* the back free fragment */
                newblk = get_slot(info);
-               newblk->start = (void *)e;
+               newblk->start = e;
                newblk->size = be - e;
 
                list_add(&newblk->list, &blk->list);
        }
 
-       return (void *)s;
+       return s;
 }
+EXPORT_SYMBOL_GPL(rh_detach_region);
 
-void *rh_alloc_align(rh_info_t * info, int size, int alignment, const char *owner)
+/* Allocate a block of memory at the specified alignment.  The value returned
+ * is an offset into the buffer initialized by rh_init(), or a negative number
+ * if there is an error.
+ */
+unsigned long rh_alloc_align(rh_info_t * info, int size, int alignment, const char *owner)
 {
        struct list_head *l;
        rh_block_t *blk;
        rh_block_t *newblk;
-       void *start;
+       unsigned long start, sp_size;
 
-       /* Validate size, (must be power of two) */
+       /* Validate size, and alignment must be power of two */
        if (size <= 0 || (alignment & (alignment - 1)) != 0)
-               return ERR_PTR(-EINVAL);
-
-       /* given alignment larger that default rheap alignment */
-       if (alignment > info->alignment)
-               size += alignment - 1;
+               return (unsigned long) -EINVAL;
 
        /* Align to configured alignment */
        size = (size + (info->alignment - 1)) & ~(info->alignment - 1);
 
-       if (assure_empty(info, 1) < 0)
-               return ERR_PTR(-ENOMEM);
+       if (assure_empty(info, 2) < 0)
+               return (unsigned long) -ENOMEM;
 
        blk = NULL;
        list_for_each(l, &info->free_list) {
                blk = list_entry(l, rh_block_t, list);
-               if (size <= blk->size)
-                       break;
+               if (size <= blk->size) {
+                       start = (blk->start + alignment - 1) & ~(alignment - 1);
+                       if (start + size <= blk->start + blk->size)
+                               break;
+               }
                blk = NULL;
        }
 
        if (blk == NULL)
-               return ERR_PTR(-ENOMEM);
+               return (unsigned long) -ENOMEM;
 
        /* Just fits */
        if (blk->size == size) {
                /* Move from free list to taken list */
                list_del(&blk->list);
-               blk->owner = owner;
-               start = blk->start;
-
-               attach_taken_block(info, blk);
-
-               return start;
+               newblk = blk;
+       } else {
+               /* Fragment caused, split if needed */
+               /* Create block for fragment in the beginning */
+               sp_size = start - blk->start;
+               if (sp_size) {
+                       rh_block_t *spblk;
+
+                       spblk = get_slot(info);
+                       spblk->start = blk->start;
+                       spblk->size = sp_size;
+                       /* add before the blk */
+                       list_add(&spblk->list, blk->list.prev);
+               }
+               newblk = get_slot(info);
+               newblk->start = start;
+               newblk->size = size;
+
+               /* blk still in free list, with updated start and size
+                * for fragment in the end */
+               blk->start = start + size;
+               blk->size -= sp_size + size;
+               /* No fragment in the end, remove blk */
+               if (blk->size == 0) {
+                       list_del(&blk->list);
+                       release_slot(info, blk);
+               }
        }
 
-       newblk = get_slot(info);
-       newblk->start = blk->start;
-       newblk->size = size;
        newblk->owner = owner;
-
-       /* blk still in free list, with updated start, size */
-       blk->start = (int8_t *)blk->start + size;
-       blk->size -= size;
-
-       start = newblk->start;
-
        attach_taken_block(info, newblk);
 
-       /* for larger alignment return fixed up pointer  */
-       /* this is no problem with the deallocator since */
-       /* we scan for pointers that lie in the blocks   */
-       if (alignment > info->alignment)
-               start = (void *)(((unsigned long)start + alignment - 1) &
-                               ~(alignment - 1));
-
        return start;
 }
+EXPORT_SYMBOL_GPL(rh_alloc_align);
 
-void *rh_alloc(rh_info_t * info, int size, const char *owner)
+/* Allocate a block of memory at the default alignment.  The value returned is
+ * an offset into the buffer initialized by rh_init(), or a negative number if
+ * there is an error.
+ */
+unsigned long rh_alloc(rh_info_t * info, int size, const char *owner)
 {
        return rh_alloc_align(info, size, info->alignment, owner);
 }
+EXPORT_SYMBOL_GPL(rh_alloc);
 
-/* allocate at precisely the given address */
-void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner)
+/* Allocate a block of memory at the given offset, rounded up to the default
+ * alignment.  The value returned is an offset into the buffer initialized by
+ * rh_init(), or a negative number if there is an error.
+ */
+unsigned long rh_alloc_fixed(rh_info_t * info, unsigned long start, int size, const char *owner)
 {
        struct list_head *l;
        rh_block_t *blk, *newblk1, *newblk2;
@@ -506,10 +532,10 @@ void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner)
 
        /* Validate size */
        if (size <= 0)
-               return ERR_PTR(-EINVAL);
+               return (unsigned long) -EINVAL;
 
        /* The region must be aligned */
-       s = (unsigned long)start;
+       s = start;
        e = s + size;
        m = info->alignment - 1;
 
@@ -520,20 +546,20 @@ void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner)
        e = e & ~m;
 
        if (assure_empty(info, 2) < 0)
-               return ERR_PTR(-ENOMEM);
+               return (unsigned long) -ENOMEM;
 
        blk = NULL;
        list_for_each(l, &info->free_list) {
                blk = list_entry(l, rh_block_t, list);
                /* The range must lie entirely inside one free block */
-               bs = (unsigned long)blk->start;
-               be = (unsigned long)blk->start + blk->size;
+               bs = blk->start;
+               be = blk->start + blk->size;
                if (s >= bs && e <= be)
                        break;
        }
 
        if (blk == NULL)
-               return ERR_PTR(-ENOMEM);
+               return (unsigned long) -ENOMEM;
 
        /* Perfect fit */
        if (bs == s && be == e) {
@@ -551,7 +577,7 @@ void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner)
        /* blk still in free list, with updated start and/or size */
        if (bs == s || be == e) {
                if (bs == s)
-                       blk->start = (int8_t *)blk->start + size;
+                       blk->start += size;
                blk->size -= size;
 
        } else {
@@ -560,14 +586,14 @@ void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner)
 
                /* The back free fragment */
                newblk2 = get_slot(info);
-               newblk2->start = (void *)e;
+               newblk2->start = e;
                newblk2->size = be - e;
 
                list_add(&newblk2->list, &blk->list);
        }
 
        newblk1 = get_slot(info);
-       newblk1->start = (void *)s;
+       newblk1->start = s;
        newblk1->size = e - s;
        newblk1->owner = owner;
 
@@ -576,8 +602,13 @@ void *rh_alloc_fixed(rh_info_t * info, void *start, int size, const char *owner)
 
        return start;
 }
+EXPORT_SYMBOL_GPL(rh_alloc_fixed);
 
-int rh_free(rh_info_t * info, void *start)
+/* Deallocate the memory previously allocated by one of the rh_alloc functions.
+ * The return value is the size of the deallocated block, or a negative number
+ * if there is an error.
+ */
+int rh_free(rh_info_t * info, unsigned long start)
 {
        rh_block_t *blk, *blk2;
        struct list_head *l;
@@ -604,6 +635,7 @@ int rh_free(rh_info_t * info, void *start)
 
        return size;
 }
+EXPORT_SYMBOL_GPL(rh_free);
 
 int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
 {
@@ -641,8 +673,9 @@ int rh_get_stats(rh_info_t * info, int what, int max_stats, rh_stats_t * stats)
 
        return nr;
 }
+EXPORT_SYMBOL_GPL(rh_get_stats);
 
-int rh_set_owner(rh_info_t * info, void *start, const char *owner)
+int rh_set_owner(rh_info_t * info, unsigned long start, const char *owner)
 {
        rh_block_t *blk, *blk2;
        struct list_head *l;
@@ -665,6 +698,7 @@ int rh_set_owner(rh_info_t * info, void *start, const char *owner)
 
        return size;
 }
+EXPORT_SYMBOL_GPL(rh_set_owner);
 
 void rh_dump(rh_info_t * info)
 {
@@ -684,8 +718,8 @@ void rh_dump(rh_info_t * info)
                nr = maxnr;
        for (i = 0; i < nr; i++)
                printk(KERN_INFO
-                      "    0x%p-0x%p (%u)\n",
-                      st[i].start, (int8_t *) st[i].start + st[i].size,
+                      "    0x%lx-0x%lx (%u)\n",
+                      st[i].start, st[i].start + st[i].size,
                       st[i].size);
        printk(KERN_INFO "\n");
 
@@ -695,15 +729,18 @@ void rh_dump(rh_info_t * info)
                nr = maxnr;
        for (i = 0; i < nr; i++)
                printk(KERN_INFO
-                      "    0x%p-0x%p (%u) %s\n",
-                      st[i].start, (int8_t *) st[i].start + st[i].size,
+                      "    0x%lx-0x%lx (%u) %s\n",
+                      st[i].start, st[i].start + st[i].size,
                       st[i].size, st[i].owner != NULL ? st[i].owner : "");
        printk(KERN_INFO "\n");
 }
+EXPORT_SYMBOL_GPL(rh_dump);
 
 void rh_dump_blk(rh_info_t * info, rh_block_t * blk)
 {
        printk(KERN_INFO
-              "blk @0x%p: 0x%p-0x%p (%u)\n",
-              blk, blk->start, (int8_t *) blk->start + blk->size, blk->size);
+              "blk @0x%p: 0x%lx-0x%lx (%u)\n",
+              blk, blk->start, blk->start + blk->size, blk->size);
 }
+EXPORT_SYMBOL_GPL(rh_dump_blk);
+