/* Copyright 2016 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #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, unsigned int node) {} static inline void buddy_check_alloc_down(struct buddy *b, unsigned int node) {} #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); }