L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
list_alloc
1// vim:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * (c) 2008-2009 Alexander Warg <warg@os.inf.tu-dresden.de>,
4 * Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
5 * economic rights: Technische Universität Dresden (Germany)
6 *
7 * License: see LICENSE.spdx (in this directory or the directories above)
8 */
9
10#pragma once
11
12#include <l4/cxx/arith>
13#include <l4/cxx/minmax>
14#include <l4/cxx/type_traits>
15#include <l4/sys/consts.h>
16
17#ifdef CXX_LIST_ALLOC_SANITY
18#include <l4/cxx/iostream>
19#endif
20
21namespace cxx {
22
27{
28private:
29 friend class List_alloc_sanity_guard;
30
31 struct Mem_block
32 {
33 Mem_block *next;
34 unsigned long size;
35 };
36
37 Mem_block *_first;
38
39 inline void check_overlap(void *, unsigned long) const;
40 inline void sanity_check_list(char const *, char const *,
41 bool unmerged) const;
42 inline void merge(Mem_block *, Mem_block *);
43
44public:
45
52 List_alloc() : _first(0) {}
53
66 inline void free(void *block, unsigned long size, bool initial_free = false);
67
82 inline void *alloc(unsigned long size, unsigned long align,
83 unsigned long lower = 0, unsigned long upper = ~0UL);
84
105 inline void *alloc_max(unsigned long min, unsigned long *max,
106 unsigned long align, unsigned granularity,
107 unsigned long lower = 0, unsigned long upper = ~0UL);
108
114 inline unsigned long avail() const;
115
116 template <typename DBG>
117 void dump_free_list(DBG &out) const;
118};
119
120#if !defined (CXX_LIST_ALLOC_SANITY)
121class List_alloc_sanity_guard
122{
123public:
124 List_alloc_sanity_guard(List_alloc const *, char const *, bool)
125 {}
126
127};
128
129
130void
131List_alloc::check_overlap(void *, unsigned long) const
132{}
133
134void
135List_alloc::sanity_check_list(char const *, char const *, bool) const
136{}
137
138#else
139
140class List_alloc_sanity_guard
141{
142private:
143 List_alloc const *a;
144 char const *func;
145
146public:
147 List_alloc_sanity_guard(List_alloc const *a, char const *func, bool unmerged)
148 : a(a), func(func)
149 { a->sanity_check_list(func, "entry", unmerged); }
150
151 ~List_alloc_sanity_guard()
152 { a->sanity_check_list(func, "exit", false); }
153};
154
155void
156List_alloc::check_overlap(void *b, unsigned long s) const
157{
158 unsigned long const mb_align = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1;
159 if ((unsigned long)b & mb_align)
160 {
161 L4::cerr << "List_alloc(FATAL): trying to free unaligned memory: "
162 << b << " align=" << arith::Ld<sizeof(Mem_block)>::value << "\n";
163 }
164
165 Mem_block const *c = _first;
166 for (;c ; c = c->next)
167 {
168 unsigned long x_s = (unsigned long)b;
169 unsigned long x_e = x_s + s;
170 unsigned long b_s = (unsigned long)c;
171 unsigned long b_e = b_s + c->size;
172
173 if ((x_s >= b_s && x_s < b_e)
174 || (x_e > b_s && x_e <= b_e)
175 || (b_s >= x_s && b_s < x_e)
176 || (b_e > x_s && b_e <= x_e))
177 {
178 L4::cerr << "List_alloc(FATAL): trying to free memory that "
179 "is already free: \n ["
180 << (void*)x_s << '-' << (void*)x_e << ") overlaps ["
181 << (void*)b_s << '-' << (void*)b_e << ")\n";
182 }
183 }
184}
185
186void
187List_alloc::sanity_check_list(char const *func, char const *info,
188 bool unmerged) const
189{
190 Mem_block const *c = _first;
191 for (;c ; c = c->next)
192 {
193 if (c->next)
194 {
195 if (c >= c->next)
196 {
197 L4::cerr << "List_alloc(FATAL): " << func << '(' << info
198 << "): list order violation\n";
199 }
200
201 unsigned long c_end = reinterpret_cast<unsigned long>(c) + c->size;
202 unsigned long n_start = reinterpret_cast<unsigned long>(c->next);
203 if (c_end > n_start || (c_end == n_start && !unmerged))
204 {
205 L4::cerr << "List_alloc(FATAL): " << func << '(' << info
206 << "): list order violation\n";
207 }
208 }
209 }
210}
211
212#endif
213
217void
218List_alloc::merge(Mem_block *prev, Mem_block *c)
219{
220 // We allow the list to not be merged in the beginning.
221 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__, true);
222 int iters = 1;
223 if (prev)
224 {
225 c = prev;
226 // If we have a predecessor, we might need to merge twice.
227 iters = 2;
228 }
229
230 while (c && c->next && iters--)
231 {
232 unsigned long f_start = reinterpret_cast<unsigned long>(c);
233 unsigned long f_end = f_start + c->size;
234 unsigned long n_start = reinterpret_cast<unsigned long>(c->next);
235
236 if (f_end == n_start)
237 {
238 c->size += c->next->size;
239 c->next = c->next->next;
240 continue;
241 }
242
243 c = c->next;
244 }
245}
246
247void
248List_alloc::free(void *block, unsigned long size, bool initial_free)
249{
250 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__, false);
251
252 unsigned long const mb_align = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1;
253
254 if (initial_free)
255 {
256 // enforce alignment constraint on initial memory
257 unsigned long nblock = (reinterpret_cast<unsigned long>(block) + mb_align)
258 & ~mb_align;
259 size = (size - (nblock - reinterpret_cast<unsigned long>(block)))
260 & ~mb_align;
261 block = reinterpret_cast<void*>(nblock);
262 }
263 else
264 // blow up size to the minimum aligned size
265 size = (size + mb_align) & ~mb_align;
266
267 check_overlap(block, size);
268
269 Mem_block *prev = 0;
270 Mem_block **c = &_first;
271 Mem_block *next = 0;
272
273 if (*c)
274 {
275 while (*c && *c < block)
276 {
277 prev = *c;
278 c = &prev->next;
279 }
280
281 next = *c;
282 }
283
284 *c = reinterpret_cast<Mem_block*>(block);
285
286 (*c)->next = next;
287 (*c)->size = size;
288
289 merge(prev, *c);
290}
291
292void *
293List_alloc::alloc_max(unsigned long min, unsigned long *max, unsigned long align,
294 unsigned granularity, unsigned long lower,
295 unsigned long upper)
296{
297 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__, false);
298
299 unsigned char const mb_bits = arith::Ld<sizeof(Mem_block)>::value;
300 unsigned long const mb_align = (1UL << mb_bits) - 1;
301
302 // blow minimum up to at least the minimum aligned size of a Mem_block
303 min = l4_round_size(min, mb_bits);
304 // truncate maximum to at least the size of a Mem_block
305 *max = l4_trunc_size(*max, mb_bits);
306 // truncate maximum size according to granularity
307 *max = *max & ~(granularity - 1UL);
308
309 if (min > *max)
310 return 0;
311
312 unsigned long almask = align ? (align - 1UL) : 0;
313
314 // minimum alignment is given by the size of a Mem_block
315 if (almask < mb_align)
316 almask = mb_align;
317
318 Mem_block **c = &_first;
319 Mem_block **fit = 0;
320 unsigned long max_fit = 0;
321 unsigned long a_lower = (lower + almask) & ~almask;
322
323 for (; *c; c = &(*c)->next)
324 {
325 // address of free memory block
326 unsigned long n_start = reinterpret_cast<unsigned long>(*c);
327
328 // block too small, next
329 // XXX: maybe we can skip this and just do the test below
330 if ((*c)->size < min)
331 continue;
332
333 // block outside region, next
334 if (upper < n_start || a_lower > n_start + (*c)->size)
335 continue;
336
337 // aligned start address within the free block
338 unsigned long a_start = (n_start + almask) & ~almask;
339
340 // check if aligned start address is behind the block, next
341 if (a_start - n_start >= (*c)->size)
342 continue;
343
344 a_start = a_start < a_lower ? a_lower : a_start;
345
346 // end address would overflow, next
347 if (min > ~0UL - a_start)
348 continue;
349
350 // block outside region, next
351 if (a_start + min - 1UL > upper)
352 continue;
353
354 // remaining size after subtracting the padding for the alignment
355 unsigned long r_size = (*c)->size - a_start + n_start;
356
357 // upper limit can limit maximum size
358 if (a_start + r_size - 1UL > upper)
359 r_size = upper - a_start + 1UL;
360
361 // round down according to granularity
362 r_size &= ~(granularity - 1UL);
363
364 // block too small
365 if (r_size < min)
366 continue;
367
368 if (r_size >= *max)
369 {
370 fit = c;
371 max_fit = *max;
372 break;
373 }
374
375 if (r_size > max_fit)
376 {
377 max_fit = r_size;
378 fit = c;
379 }
380 }
381
382 if (fit)
383 {
384 unsigned long n_start = reinterpret_cast<unsigned long>(*fit);
385 unsigned long a_lower = (lower + almask) & ~almask;
386 unsigned long a_start = (n_start + almask) & ~almask;
387 a_start = a_start < a_lower ? a_lower : a_start;
388 unsigned long r_size = (*fit)->size - a_start + n_start;
389
390 if (a_start > n_start)
391 {
392 (*fit)->size -= r_size;
393 fit = &(*fit)->next;
394 }
395 else
396 *fit = (*fit)->next;
397
398 *max = max_fit;
399 if (r_size == max_fit)
400 return reinterpret_cast<void *>(a_start);
401
402 Mem_block *m = reinterpret_cast<Mem_block*>(a_start + max_fit);
403 m->next = *fit;
404 m->size = r_size - max_fit;
405 *fit = m;
406 return reinterpret_cast<void *>(a_start);
407 }
408
409 return 0;
410}
411
412void *
413List_alloc::alloc(unsigned long size, unsigned long align, unsigned long lower,
414 unsigned long upper)
415{
416 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__, false);
417
418 unsigned long const mb_align
419 = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1;
420
421 // blow up size to the minimum aligned size
422 size = (size + mb_align) & ~mb_align;
423
424 unsigned long almask = align ? (align - 1UL) : 0;
425
426 // minimum alignment is given by the size of a Mem_block
427 if (almask < mb_align)
428 almask = mb_align;
429
430 Mem_block **c = &_first;
431 unsigned long a_lower = (lower + almask) & ~almask;
432
433 for (; *c; c=&(*c)->next)
434 {
435 // address of free memory block
436 unsigned long n_start = reinterpret_cast<unsigned long>(*c);
437
438 // block too small, next
439 // XXX: maybe we can skip this and just do the test below
440 if ((*c)->size < size)
441 continue;
442
443 // block outside region, next
444 if (upper < n_start || a_lower > n_start + (*c)->size)
445 continue;
446
447 // aligned start address within the free block
448 unsigned long a_start = (n_start + almask) & ~almask;
449
450 // block too small after alignment, next
451 if (a_start - n_start >= (*c)->size)
452 continue;
453
454 a_start = a_start < a_lower ? a_lower : a_start;
455
456 // end address would overflow, next
457 if (size > ~0UL - a_start)
458 continue;
459
460 // block outside region, next
461 if (a_start + size - 1UL > upper)
462 continue;
463
464 // remaining size after subtracting the padding
465 // for the alignment
466 unsigned long r_size = (*c)->size - a_start + n_start;
467
468 // block too small
469 if (r_size < size)
470 continue;
471
472 if (a_start > n_start)
473 {
474 // have free space before the allocated block
475 // shrink the block and set c to the next pointer of that
476 // block
477 (*c)->size -= r_size;
478 c = &(*c)->next;
479 }
480 else
481 // drop the block, c remains the next pointer of the
482 // previous block
483 *c = (*c)->next;
484
485 // allocated the whole remaining space
486 if (r_size == size)
487 return reinterpret_cast<void*>(a_start);
488
489 // add a new free block behind the allocated block
490 Mem_block *m = reinterpret_cast<Mem_block*>(a_start + size);
491 m->next = *c;
492 m->size = r_size - size;
493 *c = m;
494 return reinterpret_cast<void *>(a_start);
495 }
496
497 return 0;
498}
499
500unsigned long
502{
503 List_alloc_sanity_guard
504 __attribute__((unused)) guard(this, __FUNCTION__, false);
505 Mem_block const *c = _first;
506 unsigned long a = 0;
507 while (c)
508 {
509 a += c->size;
510 c = c->next;
511 }
512
513 return a;
514}
515
516template <typename DBG>
517void
518List_alloc::dump_free_list(DBG &out) const
519{
520 Mem_block const *c = _first;
521 while (c)
522 {
523 static constexpr char const *const unitstr[4] =
524 { "Byte", "KiB", "MiB", "GiB" };
525
526 unsigned sz = c->size;
527 unsigned i;
528 for (i = 0; i < cxx::array_size(unitstr) && sz > 8 << 10; ++i)
529 sz >>= 10;
530
531 out.printf("%12p - %12p (%u %s)\n",
532 c, reinterpret_cast<char const *>(c) + c->size - 1, sz, unitstr[i]);
533
534 c = c->next;
535 }
536}
537
538}
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.
Definition list_alloc:293
List_alloc()
Initializes an empty list allocator.
Definition list_alloc:52
void * alloc(unsigned long size, unsigned long align, unsigned long lower=0, unsigned long upper=~0UL)
Allocate a memory block.
Definition list_alloc:413
unsigned long avail() const
Get the amount of available memory.
Definition list_alloc:501
void free(void *block, unsigned long size, bool initial_free=false)
Return a free memory block to the allocator.
Definition list_alloc:248
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.
Definition consts.h:476
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.
Definition consts.h:501
IO Stream.
Common constants.
BasicOStream cerr
Standard error stream.
Our C++ library.
Definition arith:11
Computes the binary logarithm of the given number at compile time.
Definition arith:49