59 static constexpr bool Debug_alloc =
false;
61 static constexpr bool Print_warnings =
false;
63 static constexpr bool Print_warnings =
true;
68 static constexpr size_t Min_size = 1UL << Min_order;
73 static constexpr unsigned long Invalid_node_index = 0;
74 static constexpr unsigned long Root_node_index = 1;
81 Node_ptr() : index(Invalid_node_index), level(0) {}
82 Node_ptr(
unsigned long index,
unsigned level) : index(index), level(level)
85 Node_ptr parent()
const
88 return Node_ptr{index / 2, level + 1};
91 Node_ptr left_child()
const
94 return Node_ptr{index * 2, level - 1};
97 Node_ptr right_child()
const
100 return Node_ptr{(index * 2) + 1, level - 1};
103 bool valid()
const {
return index != Invalid_node_index; }
104 bool is_left()
const {
return index % 2 == 0; }
105 bool is_right()
const {
return index % 2 == 1; }
106 bool is_root()
const {
return index == Root_node_index; }
107 bool is_leaf()
const {
return level == 0; }
109 friend constexpr bool operator==(Node_ptr
const &lhs,
111 {
return lhs.index == rhs.index && lhs.level == rhs.level; }
113 friend constexpr bool operator!=(Node_ptr
const &lhs,
115 {
return !(lhs == rhs); }
126 return num_tree_nodes(min_addr, max_addr) *
sizeof(Order_free);
129 bool initialized()
const {
return _tree !=
nullptr; }
140 unsigned char *metadata_addr,
size_t metadata_size)
145 calc_mem_range(min_addr, max_addr, &_base, &max_mem_size);
146 _base = to_min_size_units(_base);
147 _max_mem_size = to_min_size_units(max_mem_size);
148 _max_level = cxx::arith::log2u(_max_mem_size);
149 _tree = metadata_addr;
151 __builtin_memset(_tree, 0, metadata_size);
153 if constexpr (Debug_alloc)
155 printf(
"Buddy allocator info:\n");
156 printf(
" Metadata size: %zu (%zu KiB)\n",
157 metadata_size, metadata_size / 1024);
158 printf(
" Base address: %#lx\n", from_min_size_units(_base));
159 printf(
" Max mem size: %#llx\n", _max_mem_size *
l4_uint64_t{Min_size});
160 printf(
" Max mem order: %u\n", _max_level + Min_order);
161 printf(
" Range: [%#lx, %#lx]\n", min_addr, max_addr);
162 printf(
" Max level: %u\n", _max_level);
163 printf(
" Total number of nodes: %lu\n",
164 num_tree_nodes(min_addr, max_addr));
165 printf(
" Levels:\n");
166 for (
int level = _max_level; level >= 0; level--)
168 printf(
" %2d: size=%#llx start_index=%ld, nodes=%ld\n", level,
169 block_size_for_level(level) *
l4_uint64_t{Min_size},
170 level_start_index(level), nodes_in_level(level));
183 inline void add_mem(
void *mem,
size_t size);
201 inline void *
alloc(
size_t size,
size_t align,
l4_addr_t lower = 0,
231 inline void *
alloc_max(
size_t min,
size_t *max,
size_t align,
244 inline void free(
void *block,
size_t size);
251 inline size_t avail()
const;
253 template<
typename DBG>
254 void dump_free(DBG &out)
const;
262 Mem_range() : start(static_cast<
l4_addr_t>(-1)), end(0) {}
267 {
return start <= end; }
270 {
return end - start + 1; }
272 friend bool operator < (Mem_range
const &lhs, Mem_range
const &rhs)
273 {
return lhs.end < rhs.start; }
275 bool overlaps(Mem_range
const &o)
const
276 {
return !(*
this < o) && !(o < *
this); }
278 bool contains(Mem_range
const &o)
const
279 {
return start <= o.start && end >= o.end; }
281 Mem_range intersect(Mem_range
const &o)
const
286 return Mem_range{start > o.start ? start : o.start,
287 end < o.end ? end : o.end};
293 static constexpr T ceil_power_of_two(T val)
295 return T{1} << cxx::arith::log2u_ceil(val);
299 static constexpr T trunc_power_of_two(T val)
301 return T{1} << cxx::arith::log2u(val);
305 static constexpr bool is_power_of_two(T val)
307 return val > 0 && (val & (val - 1)) == 0;
310 template<
typename T,
typename B>
311 static constexpr T ceil_to(T val, B boundary)
313 assert(is_power_of_two(boundary));
314 return (val + boundary - 1) & ~T{boundary - 1};
317 template<
typename T,
typename B>
318 static constexpr T trunc_to(T val, B boundary)
320 assert(is_power_of_two(boundary));
321 return val & ~T{boundary - 1};
324 template<
typename T,
typename B>
325 static constexpr bool is_aligned_to(T val, B boundary)
327 return trunc_to(val, boundary) == val;
330 static constexpr unsigned trailing_zeroes(
unsigned val)
332 return val ? __builtin_ctz(val) : sizeof(unsigned) * 8;
335 static constexpr unsigned trailing_zeroes(
unsigned long val)
337 return val ? __builtin_ctzl(val) : sizeof(unsigned long) * 8;
342 static constexpr T to_min_size_units(T val)
344 assert(is_aligned_to(val, Min_size));
345 return val / Min_size;
350 static constexpr T from_min_size_units(T val)
352 assert((val * Min_size) / Min_size == val);
353 return val * Min_size;
371 assert(min_addr < max_addr);
373 assert(raw_size >= Min_size);
375 *max_size = ceil_power_of_two(raw_size);
376 *base_addr = trunc_to(
l4_uint64_t{min_addr}, *max_size);
381 while (*max_size <= max_addr - *base_addr)
386 *base_addr = trunc_to(
l4_uint64_t{min_addr}, *max_size);
390 static constexpr unsigned long num_tree_nodes(
l4_addr_t min_addr,
395 calc_mem_range(min_addr, max_addr, &base_addr, &max_mem_size);
398 auto actual_nodes = ((2 * max_mem_size) - 1) / Min_size;
401 return actual_nodes + 1;
404 unsigned long nodes_in_level(
unsigned level)
const
406 return 1UL << (_max_level - level);
409 unsigned long level_start_index(
unsigned level)
const
411 return nodes_in_level(level);
414 static unsigned level_for_block_size(
size_t size)
416 assert(is_power_of_two(size));
417 return cxx::arith::log2u(size);
428 static l4_size_t block_size_for_level(
unsigned level)
433 Node_ptr node_for_block_level(
l4_addr_t addr,
unsigned level)
const
435 size_t node_size = block_size_for_level(level);
437 assert(addr >= _base);
438 assert(addr + node_size > addr);
439 assert(addr + node_size <= _base + _max_mem_size);
440 assert(is_aligned_to(addr, node_size));
442 unsigned long index = level_start_index(level) + ((addr - _base) / node_size);
443 return Node_ptr{index, level};
446 Node_ptr node_for_block(
l4_addr_t addr,
size_t node_size)
const
448 assert(addr >= _base);
449 assert(addr + node_size > addr);
450 assert(addr + node_size <= _base + _max_mem_size);
451 assert(is_aligned_to(addr, node_size));
453 unsigned level = level_for_block_size(node_size);
454 unsigned long index = level_start_index(level) + ((addr - _base) / node_size);
455 return Node_ptr{index, level};
458 l4_addr_t block_start_addr(Node_ptr node)
const
460 size_t size = block_size_for_level(node.level);
461 size_t offset = (node.index - level_start_index(node.level)) * size;
462 return _base + offset;
465 l4_addr_t block_end_addr(Node_ptr node)
const
467 l4_addr_t block_start = block_start_addr(node);
468 size_t block_size = block_size_for_level(node.level);
469 return block_start + block_size - 1;
472 Mem_range block_range(Node_ptr node)
const
474 return Mem_range{block_start_addr(node), block_end_addr(node)};
477 static bool fits_level(Order_free node_order,
unsigned level)
481 return node_order > level;
489 static unsigned min_level_required_for_size(
size_t size)
500 return cxx::arith::log2u((size + 1) / 2);
503 Order_free node_get(Node_ptr node)
const
505 assert(node.valid());
506 return _tree[node.index];
509 void node_set(Node_ptr node, Order_free largest_order)
511 _tree[node.index] = largest_order;
520 bool node_is_free(Node_ptr node)
const
522 return node_get(node) == node.level + 1;
526 bool node_is_used(Node_ptr node)
const {
return node_get(node) == 0; }
528 void node_set_free(Node_ptr node) { node_set(node, node.level + 1); }
529 void node_set_used(Node_ptr node) { node_set(node, 0); }
531 Node_ptr root_node()
const {
return Node_ptr{Root_node_index, _max_level}; }
533 bool node_is_seperately_allocated(Node_ptr node)
539 return !node.is_leaf() && node_is_used(node.left_child())
540 && node_is_used(node.right_child());
559 template<
typename CB>
560 bool tree_walk(CB
const &cb, Node_ptr root)
const
563 auto handle_node = [&cb](Node_ptr &node)
565 Walk_dir dir = cb(node);
566 if (dir == Walk_dir::Descend && node.is_leaf())
567 dir = Walk_dir::Next;
571 if (handle_node(root) != Walk_dir::Descend)
574 Node_ptr cur = root.left_child();
577 auto dir = handle_node(cur);
578 if (dir == Walk_dir::Stop)
581 if (dir == Walk_dir::Descend)
583 cur = cur.left_child();
589 cur = cur.parent().right_child();
590 else if (cur.is_right())
593 Node_ptr parent = cur.parent();
594 while (parent != root && parent.is_right())
595 parent = parent.parent();
598 cur = parent.parent().right_child();
606 template<
typename CB>
607 void tree_walk_free(CB
const &cb)
const
609 tree_walk([
this, &cb](Node_ptr
const &node)
611 if (node_is_free(node))
614 return Walk_dir::Next;
618 return node_is_used(node) ? Walk_dir::Next : Walk_dir::Descend;
631 Node_ptr find_bounded_root(
unsigned desired_level,
632 Mem_range
const &alloc_range)
634 Node_ptr node = root_node();
635 while (node.level > desired_level)
638 if (node_is_used(node))
641 auto left = node.left_child();
642 auto right = node.right_child();
643 if (block_range(left).contains(alloc_range))
645 else if (block_range(right).contains(alloc_range))
653 static Node_ptr next_node(Node_ptr node, Node_ptr root)
655 assert(node.valid());
660 node = node.parent().right_child();
661 else if (node.is_right())
664 Node_ptr parent = node.parent();
665 while (parent != root && parent.is_right())
666 parent = parent.parent();
669 node = parent.parent().right_child();
674 static Node_ptr prev_node(Node_ptr node, Node_ptr root)
676 assert(node.valid());
681 node = node.parent().left_child();
682 else if (node.is_left())
685 Node_ptr parent = node.parent();
686 while (parent != root && parent.is_left())
687 parent = parent.parent();
690 node = parent.parent().left_child();
695 Node_ptr next_adjacent_free(Node_ptr node, Node_ptr root)
697 Node_ptr next = next_node(node, root);
701 while (!node_is_free(next))
703 if (next.is_leaf() || node_is_used(next))
705 next = next.left_child();
710 Node_ptr prev_adjacent_free(Node_ptr node, Node_ptr root)
712 Node_ptr prev = prev_node(node, root);
716 while (!node_is_free(prev))
718 if (prev.is_leaf() || node_is_used(prev))
720 prev = prev.right_child();
732 unsigned next_level = cxx::min(
733 trailing_zeroes(cur_addr),
734 trailing_zeroes(trunc_power_of_two(remaining_size)));
735 return node_for_block_level(cur_addr, next_level);
738 [[nodiscard]]
static bool normalize_size_align(
size_t &size,
size_t &align)
740 size = ceil_to(size, Min_size);
744 if (
L4_UNLIKELY(!is_power_of_two(align) || align > size))
746 align = ceil_to(align, Min_size);
751 size_t size,
size_t align)
754 l4_addr_t aligned_lower = ceil_to(lower, align);
757 lower = aligned_lower;
763 upper = trunc_to(upper + 1, Min_size) - 1;
768 return upper - lower >= size - 1;
775 l4_size_t align, Mem_range alloc_bounds, Node_ptr base_node)
780 alloc_bounds(alloc_bounds),
781 base_node(base_node),
782 min_anchor_level(min_level_required_for_size(min_size))
789 Mem_range
const alloc_bounds;
790 Node_ptr
const base_node;
794 unsigned min_anchor_level;
798 unsigned max_level = 0;
799 Mem_range free_range;
801 [[nodiscard]]
bool add_free_node(Node_ptr
const &node,
802 Mem_range
const &intersect)
804 if (!start_node.valid())
807 auto aligned_start = ceil_to(intersect.start, align);
808 if (aligned_start > intersect.end)
813 end_node = Node_ptr();
814 free_range.start = aligned_start;
815 free_range.end = intersect.end;
816 max_level = node.level;
821 assert(free_range.end + 1 == intersect.start);
822 free_range.end = intersect.end;
823 max_level = cxx::max(max_level, node.level);
828 void reset_free_range()
830 start_node = Node_ptr();
846 template<
typename CB>
847 void search(CB
const &eval_alloc);
858 inline bool try_alloc();
869 static bool may_align(
unsigned cur_level,
l4_size_t align)
871 return block_size_for_level(cur_level) > align;
874 inline void expand_start();
875 inline void expand_back();
888 inline void *alloc_size_aligned(Node_ptr
const &root,
unsigned alloc_level);
890 [[nodiscard]]
void *alloc_single_node(Node_ptr node)
892 assert(node_is_free(node));
894 update_parents_after_alloc(node);
895 return reinterpret_cast<void *
>(from_min_size_units(block_start_addr(node)));
899 void *alloc_partial(Node_ptr node,
l4_size_t partial_size)
901 assert(node_is_free(node));
902 Node_ptr cur_node = node;
906 l4_size_t node_size = block_size_for_level(cur_node.level);
908 if (remaining_size == node_size)
910 node_set_used(cur_node);
911 remaining_size -= node_size;
912 update_parents_after_alloc(cur_node);
916 else if (remaining_size > node_size / 2)
918 node_set_used(cur_node.left_child());
919 remaining_size -= node_size / 2;
920 cur_node = cur_node.right_child();
924 cur_node = cur_node.left_child();
926 return reinterpret_cast<void *
>(from_min_size_units(block_start_addr(node)));
930 [[nodiscard]]
void *alloc_node_range(Node_ptr node,
l4_size_t size)
932 void *result =
nullptr;
933 l4_addr_t cur_addr = block_start_addr(node);
935 while (remaining_size > 0)
937 assert(node_is_free(node));
938 assert(cur_addr == block_start_addr(node));
939 l4_size_t node_size = block_size_for_level(node.level);
940 assert(node_size <= remaining_size);
942 void *node_ptr = alloc_single_node(node);
943 if (result ==
nullptr)
946 cur_addr += node_size;
947 remaining_size -= node_size;
948 if (remaining_size == 0)
951 node = next_node_in_range(cur_addr, remaining_size);
970 inline void *alloc_from_search_range(Node_ptr start_node, Node_ptr end_node,
973 [[nodiscard]]
bool free_single_node(Node_ptr node)
976 if (
L4_UNLIKELY(!node_is_used(node) || node_is_seperately_allocated(node)))
981 update_parents_after_free(node);
986 void update_parents_after_alloc(Node_ptr node)
989 while (!cur.is_root())
991 Node_ptr parent = cur.parent();
992 Order_free largest_order_left = node_get(parent.left_child());
993 Order_free largest_order_right = node_get(parent.right_child());
994 Order_free new_largest_order = cxx::max(largest_order_left,
995 largest_order_right);
996 if (new_largest_order == node_get(parent))
999 node_set(parent, new_largest_order);
1005 void update_parents_after_free(Node_ptr node)
1007 Node_ptr cur = node;
1009 while (!cur.is_root())
1011 Node_ptr parent = cur.parent();
1012 Order_free largest_order_left = node_get(parent.left_child());
1013 Order_free largest_order_right = node_get(parent.right_child());
1014 bool left_free = largest_order_left == cur.level + 1;
1015 if (largest_order_left == largest_order_right && left_free)
1018 node_set_free(parent);
1022 Order_free new_largest_order = cxx::max(largest_order_left,
1023 largest_order_right);
1024 if (new_largest_order == node_get(parent))
1027 node_set(parent, new_largest_order);
1039 void mark_subtree_free(Node_ptr node)
1043 unsigned long first_index = node.index;
1044 unsigned long nodes_in_level = 1;
1045 for (
unsigned level = node.level;; --level)
1047 __builtin_memset(&_tree[first_index], level + 1, nodes_in_level);
1051 nodes_in_level *= 2;
1054 update_parents_after_free(node);
1059 unsigned _max_level;
1410 if (
L4_UNLIKELY(!is_power_of_two(granularity) || granularity > align))
1412 granularity = ceil_to(granularity, Min_size);
1414 if (
L4_UNLIKELY(!normalize_size_align(min, align)))
1420 min = ceil_to(min, granularity);
1424 l4_size_t max_ = trunc_to(*max, granularity);
1428 if (
L4_UNLIKELY(!normalize_bounds(lower, upper, min, align)))
1432 min = to_min_size_units(min);
1433 max_ = to_min_size_units(max_);
1434 align = to_min_size_units(align);
1435 granularity = to_min_size_units(granularity);
1436 lower = to_min_size_units(lower);
1437 upper = upper / Min_size;
1439 unsigned size_aligned_max_level = level_for_block_size(trunc_power_of_two(max_));
1440 Mem_range alloc_range(lower, upper);
1441 Node_ptr base_node = find_bounded_root(size_aligned_max_level, alloc_range);
1442 if (alloc_range.contains(block_range(base_node)) && is_power_of_two(max_))
1444 if (
void *block = alloc_size_aligned(base_node, size_aligned_max_level);
1447 *max = from_min_size_units(max_);
1457 Node_ptr best_start_node;
1458 Node_ptr best_end_node;
1459 Mem_range best_free_range;
1461 Alloc_walk walk(*
this, min, max_, align, alloc_range, base_node);
1464 if (!walk.try_alloc())
1469 if (!best_start_node.valid()
1470 || walk.free_range.size() > best_free_range.size())
1472 best_start_node = walk.start_node;
1473 best_end_node = walk.end_node;
1474 best_free_range = walk.free_range;
1476 if (best_free_range.size() >= max_)
1480 walk.min_anchor_level = min_level_required_for_size(
1481 best_free_range.size());
1485 walk.reset_free_range();
1489 if (!best_start_node.valid())
1492 max_ = cxx::min(walk.max_size, best_free_range.size());
1493 max_ = trunc_to(max_, granularity);
1494 *max = from_min_size_units(max_);
1495 return alloc_from_search_range(best_start_node, best_end_node,
1496 best_free_range, max_);