Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Merge branch 'devel'
[palacios.git] / kitten / mm / buddy.c
diff --git a/kitten/mm/buddy.c b/kitten/mm/buddy.c
new file mode 100644 (file)
index 0000000..ad132cb
--- /dev/null
@@ -0,0 +1,274 @@
+/* Copyright (c) 2007, Sandia National Laboratories */
+
+#include <lwk/kernel.h>
+#include <lwk/log2.h>
+#include <lwk/buddy.h>
+#include <lwk/bootmem.h>
+
+
+/**
+ * Each free block has one of these structures at its head. The link member
+ * provides linkage for the mp->avail[order] free list, where order is the
+ * size of the free block.
+ */
+struct block {
+       struct list_head link;
+       unsigned long    order;
+};
+
+
+/**
+ * Converts a block address to its block index in the specified buddy allocator.
+ * A block's index is used to find the block's tag bit, mp->tag_bits[block_id].
+ */
+static unsigned long
+block_to_id(struct buddy_mempool *mp, struct block *block)
+{
+       unsigned long block_id =
+               ((unsigned long)block - mp->base_addr) >> mp->min_order;
+       BUG_ON(block_id >= mp->num_blocks);
+       return block_id;
+}
+
+
+/**
+ * Marks a block as free by setting its tag bit to one.
+ */
+static void
+mark_available(struct buddy_mempool *mp, struct block *block)
+{
+       __set_bit(block_to_id(mp, block), mp->tag_bits);
+}
+
+
+/**
+ * Marks a block as allocated by setting its tag bit to zero.
+ */
+static void
+mark_allocated(struct buddy_mempool *mp, struct block *block)
+{
+       __clear_bit(block_to_id(mp, block), mp->tag_bits);
+}
+
+
+/**
+ * Returns true if block is free, false if it is allocated.
+ */
+static int
+is_available(struct buddy_mempool *mp, struct block *block)
+{
+       return test_bit(block_to_id(mp, block), mp->tag_bits);
+}
+
+
+/**
+ * Returns the address of the block's buddy block.
+ */
+static void *
+find_buddy(struct buddy_mempool *mp, struct block *block, unsigned long order)
+{
+       unsigned long _block;
+       unsigned long _buddy;
+
+       BUG_ON((unsigned long)block < mp->base_addr);
+
+       /* Fixup block address to be zero-relative */
+       _block = (unsigned long)block - mp->base_addr;
+
+       /* Calculate buddy in zero-relative space */
+       _buddy = _block ^ (1UL << order);
+
+       /* Return the buddy's address */
+       return (void *)(_buddy + mp->base_addr);
+}
+
+
+/**
+ * Initializes a buddy system memory allocator object.
+ *
+ * Arguments:
+ *       [IN] base_addr:  Base address of the memory pool.
+ *       [IN] pool_order: Size of the memory pool (2^pool_order bytes).
+ *       [IN] min_order:  Minimum allocatable block size (2^min_order bytes).
+ *
+ * Returns:
+ *       Success: Pointer to an initialized buddy system memory allocator.
+ *       Failure: NULL
+ *
+ * NOTE: The min_order argument is provided as an optimization. Since one tag
+ *       bit is required for each minimum-sized block, large memory pools that
+ *       allow order 0 allocations will use large amounts of memory. Specifying
+ *       a min_order of 5 (32 bytes), for example, reduces the number of tag
+ *       bits by 32x.
+ */
+struct buddy_mempool *
+buddy_init(
+       unsigned long base_addr,
+       unsigned long pool_order,
+       unsigned long min_order
+)
+{
+       struct buddy_mempool *mp;
+       unsigned long i;
+
+       /* Smallest block size must be big enough to hold a block structure */
+       if ((1UL << min_order) < sizeof(struct block))
+               min_order = ilog2( roundup_pow_of_two(sizeof(struct block)) );
+
+       /* The minimum block order must be smaller than the pool order */
+       if (min_order > pool_order)
+               return NULL;
+
+       mp = alloc_bootmem(sizeof(struct buddy_mempool));
+       
+       mp->base_addr  = base_addr;
+       mp->pool_order = pool_order;
+       mp->min_order  = min_order;
+
+       /* Allocate a list for every order up to the maximum allowed order */
+       mp->avail = alloc_bootmem((pool_order + 1) * sizeof(struct list_head));
+
+       /* Initially all lists are empty */
+       for (i = 0; i <= pool_order; i++)
+               INIT_LIST_HEAD(&mp->avail[i]);
+
+       /* Allocate a bitmap with 1 bit per minimum-sized block */
+       mp->num_blocks = (1UL << pool_order) / (1UL << min_order);
+       mp->tag_bits   = alloc_bootmem(
+                            BITS_TO_LONGS(mp->num_blocks) * sizeof(long)
+                        );
+
+       /* Initially mark all minimum-sized blocks as allocated */
+       bitmap_zero(mp->tag_bits, mp->num_blocks);
+
+       return mp;
+}
+
+
+/**
+ * Allocates a block of memory of the requested size (2^order bytes).
+ *
+ * Arguments:
+ *       [IN] mp:    Buddy system memory allocator object.
+ *       [IN] order: Block size to allocate (2^order bytes).
+ *
+ * Returns:
+ *       Success: Pointer to the start of the allocated memory block.
+ *       Failure: NULL
+ */
+void *
+buddy_alloc(struct buddy_mempool *mp, unsigned long order)
+{
+       unsigned long j;
+       struct list_head *list;
+       struct block *block;
+       struct block *buddy_block;
+
+       BUG_ON(mp == NULL);
+       BUG_ON(order > mp->pool_order);
+
+       /* Fixup requested order to be at least the minimum supported */
+       if (order < mp->min_order)
+               order = mp->min_order;
+
+       for (j = order; j <= mp->pool_order; j++) {
+
+               /* Try to allocate the first block in the order j list */
+               list = &mp->avail[j];
+               if (list_empty(list))
+                       continue;
+               block = list_entry(list->next, struct block, link);
+               list_del(&block->link);
+               mark_allocated(mp, block);
+
+               /* Trim if a higher order block than necessary was allocated */
+               while (j > order) {
+                       --j;
+                       buddy_block = (struct block *)((unsigned long)block + (1UL << j));
+                       buddy_block->order = j;
+                       mark_available(mp, buddy_block);
+                       list_add(&buddy_block->link, &mp->avail[j]);
+               }
+
+               return block;
+       }
+
+       return NULL;
+}
+
+
+/**
+ * Returns a block of memory to the buddy system memory allocator.
+ *
+ * Arguments:
+ *       [IN] mp:    Buddy system memory allocator object.
+ *       [IN] addr:  Address of memory block to free.
+ *       [IN] order: Size of the memory block (2^order bytes).
+ */
+void
+buddy_free(struct buddy_mempool *mp, void *addr, unsigned long order)
+{
+       struct block *block;
+       struct block *buddy;
+
+       BUG_ON(mp == NULL);
+       BUG_ON(order > mp->pool_order);
+
+       /* Fixup requested order to be at least the minimum supported */
+       if (order < mp->min_order)
+               order = mp->min_order;
+
+       /* Overlay block structure on the memory block being freed */
+       block = addr;
+       BUG_ON(is_available(mp, block));
+
+       /* Coalesce as much as possible with adjacent free buddy blocks */
+       while (order < mp->pool_order) {
+               /* Determine our buddy block's address */
+               buddy = find_buddy(mp, block, order);
+
+               /* Make sure buddy is available and has the same size as us */
+               if (!is_available(mp, buddy))
+                       break;
+               if (is_available(mp, buddy) && (buddy->order != order))
+                       break;
+
+               /* OK, we're good to go... buddy merge! */
+               list_del(&buddy->link);
+               if (buddy < block)
+                       block = buddy;
+               ++order;
+               block->order = order;
+       }
+
+       /* Add the (possibly coalesced) block to the appropriate free list */
+       block->order = order;
+       mark_available(mp, block);
+       list_add(&block->link, &mp->avail[order]);
+}
+
+
+/**
+ * Dumps the state of a buddy system memory allocator object to the console.
+ */
+void
+buddy_dump_mempool(struct buddy_mempool *mp)
+{
+       unsigned long i;
+       unsigned long num_blocks;
+       struct list_head *entry;
+
+       printk(KERN_DEBUG "DUMP OF BUDDY MEMORY POOL:\n");
+
+       for (i = mp->min_order; i <= mp->pool_order; i++) {
+
+               /* Count the number of memory blocks in the list */
+               num_blocks = 0;
+               list_for_each(entry, &mp->avail[i])
+                       ++num_blocks;
+
+               printk(KERN_DEBUG "  order %2lu: %lu free blocks\n", i, num_blocks);
+       }
+}
+
+