aboutsummaryrefslogtreecommitdiffstats
path: root/roms/skiboot/core/buddy.c
diff options
context:
space:
mode:
Diffstat (limited to 'roms/skiboot/core/buddy.c')
-rw-r--r--roms/skiboot/core/buddy.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/roms/skiboot/core/buddy.c b/roms/skiboot/core/buddy.c
new file mode 100644
index 000000000..b36e407d1
--- /dev/null
+++ b/roms/skiboot/core/buddy.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+/* Copyright 2016-2017 IBM Corp. */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "buddy.h"
+
+#define BUDDY_DEBUG
+#undef BUDDY_VERBOSE
+
+#ifdef BUDDY_VERBOSE
+#define BUDDY_NOISE(fmt...) printf(fmt)
+#else
+#define BUDDY_NOISE(fmt...) do { } while(0)
+#endif
+
+static inline unsigned int buddy_map_size(struct buddy *b)
+{
+ return 1u << (b->max_order + 1);
+}
+
+static inline unsigned int buddy_order_start(struct buddy *b,
+ unsigned int order)
+{
+ unsigned int level = b->max_order - order;
+
+ /* Starting bit of index for order */
+ return 1u << level;
+}
+
+static inline unsigned int buddy_index_to_node(struct buddy *b,
+ unsigned int index,
+ unsigned int order)
+{
+ /* Ensure the index is a multiple of the order */
+ assert((index & ((1u << order) - 1)) == 0);
+
+ return buddy_order_start(b, order) + (index >> order);
+}
+
+static inline unsigned int buddy_node_to_index(struct buddy *b,
+ unsigned int node,
+ unsigned int order)
+{
+ unsigned int start = buddy_order_start(b, order);
+
+ return (node - start) << order;
+}
+
+#ifdef BUDDY_DEBUG
+static void buddy_check_alloc(struct buddy *b, unsigned int node)
+{
+ assert(bitmap_tst_bit(b->map, node));
+}
+
+static void buddy_check_alloc_down(struct buddy *b, unsigned int node)
+{
+ unsigned int i, count = 1;
+
+ while (node < buddy_map_size(b)) {
+ for (i = 0; i < count; i++)
+ buddy_check_alloc(b, node + i);
+
+ /* Down one level */
+ node <<= 1;
+ count <<= 1;
+ }
+}
+#else
+static inline void buddy_check_alloc(struct buddy *b __unused, unsigned int node __unused) {}
+static inline void buddy_check_alloc_down(struct buddy *b __unused, unsigned int node __unused) {}
+#endif
+
+int buddy_alloc(struct buddy *b, unsigned int order)
+{
+ unsigned int o;
+ int node, index;
+
+ BUDDY_NOISE("buddy_alloc(%d)\n", order);
+ /*
+ * Find the first order up the tree from our requested order that
+ * has at least one free node.
+ */
+ for (o = order; o <= b->max_order; o++) {
+ if (b->freecounts[o] > 0)
+ break;
+ }
+
+ /* Nothing found ? fail */
+ if (o > b->max_order) {
+ BUDDY_NOISE(" no free nodes !\n");
+ return -1;
+ }
+
+ BUDDY_NOISE(" %d free node(s) at order %d, bits %d(%d)\n",
+ b->freecounts[o], o,
+ buddy_order_start(b, o),
+ 1u << (b->max_order - o));
+
+ /* Now find a free node */
+ node = bitmap_find_zero_bit(b->map, buddy_order_start(b, o),
+ 1u << (b->max_order - o));
+
+ /* There should always be one */
+ assert(node >= 0);
+
+ /* Mark it allocated and decrease free count */
+ bitmap_set_bit(b->map, node);
+ b->freecounts[o]--;
+
+ /* We know that node was free which means all its children must have
+ * been marked "allocated". Double check.
+ */
+ buddy_check_alloc_down(b, node);
+
+ /* We have a node, we've marked it allocated, now we need to go down
+ * the tree until we reach "order" which is the order we need. For
+ * each level along the way, we mark the buddy free and leave the
+ * first child allocated.
+ */
+ while (o > order) {
+ /* Next level down */
+ o--;
+ node <<= 1;
+
+ BUDDY_NOISE(" order %d, using %d marking %d free\n",
+ o, node, node ^ 1);
+ bitmap_clr_bit(b->map, node ^ 1);
+ b->freecounts[o]++;
+ assert(bitmap_tst_bit(b->map, node));
+ }
+
+ index = buddy_node_to_index(b, node, order);
+
+ BUDDY_NOISE(" result is index %d (node %d)\n", index, node);
+
+ /* We have a node, convert it to an element number */
+ return index;
+}
+
+bool buddy_reserve(struct buddy *b, unsigned int index, unsigned int order)
+{
+ unsigned int node, freenode, o;
+
+ assert(index < (1u << b->max_order));
+
+ BUDDY_NOISE("buddy_reserve(%d,%d)\n", index, order);
+
+ /* Get bit number for node */
+ node = buddy_index_to_node(b, index, order);
+
+ BUDDY_NOISE(" node=%d\n", node);
+
+ /* Find something free */
+ for (freenode = node, o = order; freenode > 0; freenode >>= 1, o++)
+ if (!bitmap_tst_bit(b->map, freenode))
+ break;
+
+ BUDDY_NOISE(" freenode=%d order %d\n", freenode, o);
+
+ /* Nothing free, error out */
+ if (!freenode)
+ return false;
+
+ /* We sit on a free node, mark it busy */
+ bitmap_set_bit(b->map, freenode);
+ assert(b->freecounts[o]);
+ b->freecounts[o]--;
+
+ /* We know that node was free which means all its children must have
+ * been marked "allocated". Double check.
+ */
+ buddy_check_alloc_down(b, freenode);
+
+ /* Reverse-walk the path and break down nodes */
+ while (o > order) {
+ /* Next level down */
+ o--;
+ freenode <<= 1;
+
+ /* Find the right one on the path to node */
+ if (node & (1u << (o - order)))
+ freenode++;
+
+ BUDDY_NOISE(" order %d, using %d marking %d free\n",
+ o, freenode, freenode ^ 1);
+ bitmap_clr_bit(b->map, freenode ^ 1);
+ b->freecounts[o]++;
+ assert(bitmap_tst_bit(b->map, node));
+ }
+ assert(node == freenode);
+
+ return true;
+}
+
+void buddy_free(struct buddy *b, unsigned int index, unsigned int order)
+{
+ unsigned int node;
+
+ assert(index < (1u << b->max_order));
+
+ BUDDY_NOISE("buddy_free(%d,%d)\n", index, order);
+
+ /* Get bit number for node */
+ node = buddy_index_to_node(b, index, order);
+
+ BUDDY_NOISE(" node=%d\n", node);
+
+ /* We assume that anything freed was fully allocated, ie,
+ * there is no child node of that allocation index/order
+ * that is already free.
+ *
+ * BUDDY_DEBUG will verify it at the cost of performances
+ */
+ buddy_check_alloc_down(b, node);
+
+ /* Propagate if buddy is free */
+ while (order < b->max_order && !bitmap_tst_bit(b->map, node ^ 1)) {
+ BUDDY_NOISE(" order %d node %d buddy %d free, propagating\n",
+ order, node, node ^ 1);
+
+ /* Mark buddy busy (we are already marked busy) */
+ bitmap_set_bit(b->map, node ^ 1);
+
+ /* Reduce free count */
+ assert(b->freecounts[order] > 0);
+ b->freecounts[order]--;
+
+ /* Get parent */
+ node >>= 1;
+ order++;
+
+ /* It must be busy already ! */
+ buddy_check_alloc(b, node);
+
+ BUDDY_NOISE(" testing order %d node %d\n", order, node ^ 1);
+ }
+
+ /* No more coalescing, mark it free */
+ bitmap_clr_bit(b->map, node);
+
+ /* Increase the freelist count for that level */
+ b->freecounts[order]++;
+
+ BUDDY_NOISE(" free count at order %d is %d\n",
+ order, b->freecounts[order]);
+}
+
+void buddy_reset(struct buddy *b)
+{
+ unsigned int bsize = BITMAP_BYTES(1u << (b->max_order + 1));
+
+ BUDDY_NOISE("buddy_reset()\n");
+ /* We fill the bitmap with 1's to make it completely "busy" */
+ memset(b->map, 0xff, bsize);
+ memset(b->freecounts, 0, sizeof(b->freecounts));
+
+ /* We mark the root of the tree free, this is entry 1 as entry 0
+ * is unused.
+ */
+ buddy_free(b, 0, b->max_order);
+}
+
+struct buddy *buddy_create(unsigned int max_order)
+{
+ struct buddy *b;
+ unsigned int bsize;
+
+ assert(max_order <= BUDDY_MAX_ORDER);
+
+ bsize = BITMAP_BYTES(1u << (max_order + 1));
+
+ b = zalloc(sizeof(struct buddy) + bsize);
+ if (!b)
+ return NULL;
+ b->max_order = max_order;
+
+ BUDDY_NOISE("Map @%p, size: %d bytes\n", b->map, bsize);
+
+ buddy_reset(b);
+
+ return b;
+}
+
+void buddy_destroy(struct buddy *b)
+{
+ free(b);
+}
+