15#include <l4/cxx/arith>
18#include <l4/cxx/minmax>
19#include <l4/cxx/type_traits>
31 friend class Tree_alloc_sanity_guard;
37 unsigned long addr() {
return reinterpret_cast<unsigned long>(
this); }
38 unsigned long end() {
return addr() + size; }
41 struct Mem_block_get_key
43 typedef unsigned long Key_type;
44 static Key_type key_of(Mem_block
const *e)
46 return reinterpret_cast<unsigned long>(e);
52 inline void check_overlap(
void *,
unsigned long)
const;
53 inline void sanity_check_list(
char const *,
char const *,
54 bool unmerged =
true)
const;
55 inline void merge(Mem_block *, Mem_block *);
70 inline void free(
void *block,
unsigned long size,
bool initial_free =
false);
86 inline void *
alloc(
unsigned long size,
unsigned long align,
87 unsigned long lower = 0,
unsigned long upper = ~0UL);
109 inline void *
alloc_max(
unsigned long min,
unsigned long *max,
110 unsigned long align,
unsigned granularity,
111 unsigned long lower = 0,
unsigned long upper = ~0UL);
118 inline unsigned long avail()
const;
120 template<
typename DBG>
121 void dump_free_list(DBG &out)
const;
124#if !defined(CXX_TREE_ALLOC_SANITY)
125class Tree_alloc_sanity_guard
128 Tree_alloc_sanity_guard(
Tree_alloc const *,
char const *,
bool =
true) {}
132Tree_alloc::check_overlap(
void *,
unsigned long)
const
136Tree_alloc::sanity_check_list(
char const *,
char const *,
bool)
const
141class Tree_alloc_sanity_guard
148 Tree_alloc_sanity_guard(Tree_alloc
const *a,
char const *func,
149 bool unmerged =
false)
152 a->sanity_check_list(func,
"entry", unmerged);
155 ~Tree_alloc_sanity_guard() { a->sanity_check_list(func,
"exit"); }
159Tree_alloc::check_overlap(
void *b,
unsigned long s)
const
161 unsigned long const mb_align =
162 (1UL << arith::Ld<
sizeof(Mem_block)>::value) - 1;
163 if (
reinterpret_cast<unsigned long>(b) & mb_align)
165 L4::cerr <<
"Tree_alloc(FATAL): trying to free unaligned memory: " << b
166 <<
" align=" << arith::Ld<
sizeof(Mem_block)>::value <<
"\n";
169 for (
auto const &c : tree)
171 unsigned long x_s = (
unsigned long)b;
172 unsigned long x_e = x_s + s;
173 unsigned long b_s = (
unsigned long)&c;
174 unsigned long b_e = b_s + c.size;
176 if ((x_s >= b_s && x_s < b_e) || (x_e > b_s && x_e <= b_e)
177 || (b_s >= x_s && b_s < x_e) || (b_e > x_s && b_e <= x_e))
179 L4::cerr <<
"Tree_alloc(FATAL): trying to free memory that "
180 "is already free: \n ["
181 << (
void *)x_s <<
'-' << (
void *)x_e <<
") overlaps ["
182 << (
void *)b_s <<
'-' << (
void *)b_e <<
")\n";
188Tree_alloc::sanity_check_list(
char const *func,
char const *info,
bool unmerged)
const
190 for (
auto c = tree.begin(); c != tree.end(); ++c)
194 if (next != tree.end())
196 if (c->addr() >= next->addr())
198 L4::cerr <<
"Tree_alloc(FATAL): " << func <<
'(' << info
199 <<
"): list order violation\n";
202 auto cmp = +[](
unsigned long a,
unsigned long b) {
return a >= b; };
204 cmp = +[](
unsigned long a,
unsigned long b) {
return a > b; };
205 if (cmp(c->addr() + c->size, next->addr()))
207 L4::cerr <<
"Tree_alloc(FATAL): " << func <<
'(' << info
208 <<
"): list order violation\n";
219 [[maybe_unused]] Tree_alloc_sanity_guard guard(
this, __func__);
221 unsigned long const mb_align =
222 (1UL <<
arith::Ld<
sizeof(Mem_block)>::value) - 1;
227 unsigned long nblock =
228 (
reinterpret_cast<unsigned long>(block) + mb_align) & ~mb_align;
230 (size - (nblock -
reinterpret_cast<unsigned long>(block))) & ~mb_align;
231 block =
reinterpret_cast<void *
>(nblock);
235 size = (size + mb_align) & ~mb_align;
237 check_overlap(block, size);
240 auto addr =
reinterpret_cast<unsigned long>(block);
242 auto lower = _tree.last_less_equal_node(addr);
243 if (lower && lower->end() == addr)
250 m =
reinterpret_cast<Mem_block *
>(block);
256 auto next = _tree.remove(m->end());
258 m->size += next->size;
263 unsigned long align,
unsigned granularity,
264 unsigned long lower,
unsigned long upper)
266 [[maybe_unused]] Tree_alloc_sanity_guard guard(
this, __func__);
268 unsigned char const mb_bits =
arith::Ld<
sizeof(Mem_block)>::value;
269 unsigned long const mb_align = (1UL << mb_bits) - 1;
276 *max = *max & ~(granularity - 1UL);
281 unsigned long almask = align ? (align - 1UL) : 0;
284 if (almask < mb_align)
287 Mem_block *fit =
nullptr;
288 unsigned long max_fit = 0;
289 unsigned long a_lower = (lower + almask) & ~almask;
291 for (
auto &c : _tree)
294 unsigned long n_start = c.addr();
302 if (upper < n_start || a_lower > n_start + c.size)
306 unsigned long a_start = (n_start + almask) & ~almask;
309 if (a_start - n_start >= c.size)
312 a_start = a_start < a_lower ? a_lower : a_start;
315 if (min > ~0UL - a_start)
319 if (a_start + min - 1UL > upper)
323 unsigned long r_size = c.size - a_start + n_start;
326 if (a_start + r_size - 1UL > upper)
327 r_size = upper - a_start + 1UL;
330 r_size &= ~(granularity - 1UL);
343 if (r_size > max_fit)
352 unsigned long n_start = fit->addr();
353 unsigned long a_lower = (lower + almask) & ~almask;
354 unsigned long a_start = (n_start + almask) & ~almask;
355 a_start = a_start < a_lower ? a_lower : a_start;
356 unsigned long r_size = fit->size - a_start + n_start;
358 if (a_start > n_start)
361 _tree.remove(fit->addr());
364 if (r_size == max_fit)
365 return reinterpret_cast<void *
>(a_start);
367 Mem_block *m =
reinterpret_cast<Mem_block *
>(a_start + max_fit);
368 m->size = r_size - max_fit;
370 return reinterpret_cast<void *
>(a_start);
380 [[maybe_unused]] Tree_alloc_sanity_guard guard(
this, __func__);
382 unsigned long const mb_align =
383 (1UL <<
arith::Ld<
sizeof(Mem_block)>::value) - 1;
386 size = (size + mb_align) & ~mb_align;
388 unsigned long almask = align ? (align - 1UL) : 0;
391 if (almask < mb_align)
394 unsigned long a_lower = (lower + almask) & ~almask;
396 for (
auto &c : _tree)
399 unsigned long n_start = c.addr();
407 if (upper < n_start || a_lower > n_start + c.size)
411 unsigned long a_start = (n_start + almask) & ~almask;
414 if (a_start - n_start >= c.size)
417 a_start = a_start < a_lower ? a_lower : a_start;
420 if (size > ~0UL - a_start)
424 if (a_start + size - 1UL > upper)
429 unsigned long r_size = c.size - a_start + n_start;
435 if (a_start > n_start)
441 _tree.remove(c.addr());
445 return reinterpret_cast<void *
>(a_start);
448 Mem_block *m =
reinterpret_cast<Mem_block *
>(a_start + size);
449 m->size = r_size - size;
451 return reinterpret_cast<void *
>(a_start);
460 [[maybe_unused]] Tree_alloc_sanity_guard guard(
this, __func__);
462 for (
auto const &c : _tree)
468template<
typename DBG>
470Tree_alloc::dump_free_list(DBG &out)
const
472 for (
auto const &c : _tree)
474 static constexpr char const *
const unitstr[4] =
475 {
"Byte",
"KiB",
"MiB",
"GiB" };
477 unsigned sz = c.size;
479 for (i = 0; i < cxx::array_size(unitstr) && sz > 8 << 10; ++i)
482 out.printf(
"%12p - %12p (%u %s)\n",
483 &c,
reinterpret_cast<char const *
>(&c) + c.size - 1, sz, unitstr[i]);
Standard tree-based allocator.
void * alloc_max(unsigned long min, unsigned long *max, unsigned long align, unsigned granularity, unsigned long lower=0, unsigned long upper=~0UL)
Allocate a memory block of min <= size <= max.
void * alloc(unsigned long size, unsigned long align, unsigned long lower=0, unsigned long upper=~0UL)
Allocate a memory block.
void free(void *block, unsigned long size, bool initial_free=false)
Return a free memory block to the allocator.
unsigned long avail() const
Get the amount of available memory.
l4_addr_t l4_trunc_size(l4_addr_t address, unsigned char bits) L4_NOTHROW
Round an address down to the next lower flexpage with size bits.
l4_addr_t l4_round_size(l4_addr_t value, unsigned char bits) L4_NOTHROW
Round value up to the next alignment with bits size.
BasicOStream cerr
Standard error stream.
Computes the binary logarithm of the given number at compile time.