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 * This file is part of TUD:OS and distributed under the terms of the 8 * GNU General Public License 2. 9 * Please see the COPYING-GPL-2 file for details. 10 * 11 * As a special exception, you may use this file as part of a free software 12 * library without restriction. Specifically, if other files instantiate 13 * templates or use macros or inline functions from this file, or you compile 14 * this file and link it with other files to produce an executable, this 15 * file does not by itself cause the resulting executable to be covered by 16 * the GNU General Public License. This exception does not however 17 * invalidate any other reasons why the executable file might be covered by 18 * the GNU General Public License. 19 */ 20 21#pragma once 22 23#include <l4/cxx/arith> 24#include <l4/cxx/minmax> 25 26namespace cxx { 27 28/** 29 * Standard list-based allocator. 30 */ 31class List_alloc 32{ 33private: 34 friend class List_alloc_sanity_guard; 35 36 struct Mem_block 37 { 38 Mem_block *next; 39 unsigned long size; 40 }; 41 42 Mem_block *_first; 43 44 inline void check_overlap(void *, unsigned long ); 45 inline void sanity_check_list(char const *, char const *); 46 inline void merge(); 47 48public: 49 50 /** 51 * Initializes an empty list allocator. 52 * 53 * \note To initialize the allocator with available memory 54 * use the #free() function. 55 */ 56 List_alloc() : _first(0) {} 57 58 /** 59 * Return a free memory block to the allocator. 60 * 61 * \param block Pointer to memory block. 62 * \param size Size of memory block. 63 * \param initial_free Set to true for putting fresh memory 64 * to the allocator. This will enforce alignment on that 65 * memory. 66 * 67 * \pre `block` must not be NULL. 68 * \pre `2 * sizeof(void *)` <= `size` <= `~0UL - 32`. 69 */ 70 inline void free(void *block, unsigned long size, bool initial_free = false); 71 72 /** 73 * Allocate a memory block. 74 * 75 * \param size Size of the memory block. 76 * \param align Alignment constraint. 77 * 78 * \return Pointer to memory block 79 * 80 * \pre 0 < `size` <= `~0UL - 32`. 81 */ 82 inline void *alloc(unsigned long size, unsigned long align); 83 84 /** 85 * Allocate a memory block of `min` <= size <= `max`. 86 * 87 * \param min Minimal size to allocate (in bytes). 88 * \param[in,out] max Maximum size to allocate (in bytes). The actual 89 * allocated size is returned here. 90 * \param align Alignment constraint. 91 * \param granularity Granularity to use for the allocation (power 92 * of 2). 93 * 94 * \return Pointer to memory block 95 * 96 * \pre 0 < `min` <= `~0UL - 32`. 97 * \pre 0 < `max`. 98 */ 99 inline void *alloc_max(unsigned long min, unsigned long *max, 100 unsigned long align, unsigned granularity); 101 102 /** 103 * Get the amount of available memory. 104 * 105 * \return Available memory in bytes 106 */ 107 inline unsigned long avail(); 108 109 template <typename DBG> 110 void dump_free_list(DBG &out); 111}; 112 113#if !defined (CXX_LIST_ALLOC_SANITY) 114class List_alloc_sanity_guard 115{ 116public: 117 List_alloc_sanity_guard(List_alloc *, char const *) 118 {} 119 120}; 121 122 123void 124List_alloc::check_overlap(void *, unsigned long ) 125{} 126 127void 128List_alloc::sanity_check_list(char const *, char const *) 129{} 130 131#else 132 133class List_alloc_sanity_guard 134{ 135private: 136 List_alloc *a; 137 char const *func; 138 139public: 140 List_alloc_sanity_guard(List_alloc *a, char const *func) 141 : a(a), func(func) 142 { a->sanity_check_list(func, "entry"); } 143 144 ~List_alloc_sanity_guard() 145 { a->sanity_check_list(func, "exit"); } 146}; 147 148void 149List_alloc::check_overlap(void *b, unsigned long s) 150{ 151 unsigned long const mb_align = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1; 152 if ((unsigned long)b & mb_align) 153 { 154 L4::cerr << "List_alloc(FATAL): trying to free unaligned memory: " 155 << b << " align=" << arith::Ld<sizeof(Mem_block)>::value << "\n"; 156 } 157 158 Mem_block *c = _first; 159 for (;c ; c = c->next) 160 { 161 unsigned long x_s = (unsigned long)b; 162 unsigned long x_e = x_s + s; 163 unsigned long b_s = (unsigned long)c; 164 unsigned long b_e = b_s + c->size; 165 166 if ((x_s >= b_s && x_s < b_e) 167 || (x_e > b_s && x_e <= b_e) 168 || (b_s >= x_s && b_s < x_e) 169 || (b_e > x_s && b_e <= x_e)) 170 { 171 L4::cerr << "List_alloc(FATAL): trying to free memory that " 172 "is already free: \n [" 173 << (void*)x_s << '-' << (void*)x_e << ") overlaps [" 174 << (void*)b_s << '-' << (void*)b_e << ")\n"; 175 } 176 } 177} 178 179void 180List_alloc::sanity_check_list(char const *func, char const *info) 181{ 182 Mem_block *c = _first; 183 for (;c ; c = c->next) 184 { 185 if (c->next) 186 { 187 if (c >= c->next) 188 { 189 L4::cerr << "List_alloc(FATAL): " << func << '(' << info 190 << "): list order violation\n"; 191 } 192 193 if (((unsigned long)c) + c->size > (unsigned long)c->next) 194 { 195 L4::cerr << "List_alloc(FATAL): " << func << '(' << info 196 << "): list order violation\n"; 197 } 198 } 199 } 200} 201 202#endif 203 204void 205List_alloc::merge() 206{ 207 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__); 208 Mem_block *c = _first; 209 while (c && c->next) 210 { 211 unsigned long f_start = (unsigned long)c; 212 unsigned long f_end = f_start + c->size; 213 unsigned long n_start = (unsigned long)c->next; 214 215 if (f_end == n_start) 216 { 217 c->size += c->next->size; 218 c->next = c->next->next; 219 continue; 220 } 221 222 c = c->next; 223 } 224} 225 226void 227List_alloc::free(void *block, unsigned long size, bool initial_free) 228{ 229 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__); 230 231 unsigned long const mb_align = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1; 232 233 if (initial_free) 234 { 235 // enforce alignment constraint on initial memory 236 unsigned long nblock = ((unsigned long)block + mb_align) & ~mb_align; 237 size = (size - (nblock - (unsigned long)block)) & ~mb_align; 238 block = (void*)nblock; 239 } 240 else 241 // blow up size to the minimum aligned size 242 size = (size + mb_align) & ~mb_align; 243 244 check_overlap(block, size); 245 246 Mem_block **c = &_first; 247 Mem_block *next = 0; 248 249 if (*c) 250 { 251 while (*c && *c < block) 252 c = &(*c)->next; 253 254 next = *c; 255 } 256 257 *c = (Mem_block*)block; 258 259 (*c)->next = next; 260 (*c)->size = size; 261 262 merge(); 263} 264 265void * 266List_alloc::alloc_max(unsigned long min, unsigned long *max, unsigned long align, 267 unsigned granularity) 268{ 269 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__); 270 271 unsigned char const mb_bits = arith::Ld<sizeof(Mem_block)>::value; 272 unsigned long const mb_align = (1UL << mb_bits) - 1; 273 274 // blow minimum up to at least the minimum aligned size of a Mem_block 275 min = l4_round_size(min, mb_bits); 276 // truncate maximum to at least the size of a Mem_block 277 *max = l4_trunc_size(*max, mb_bits); 278 // truncate maximum size according to granularity 279 *max = *max & ~(granularity - 1UL); 280 281 if (min > *max) 282 return 0; 283 284 unsigned long almask = align ? (align - 1UL) : 0; 285 286 // minimum alignment is given by the size of a Mem_block 287 if (almask < mb_align) 288 almask = mb_align; 289 290 Mem_block **c = &_first; 291 Mem_block **fit = 0; 292 unsigned long max_fit = 0; 293 294 for (; *c; c = &(*c)->next) 295 { 296 // address of free memory block 297 unsigned long n_start = (unsigned long)(*c); 298 299 // block too small, next 300 // XXX: maybe we can skip this and just do the test below 301 if ((*c)->size < min) 302 continue; 303 304 // aligned start address within the free block 305 unsigned long a_start = (n_start + almask) & ~almask; 306 307 // check if aligned start address is behind the block, next 308 if (a_start - n_start >= (*c)->size) 309 continue; 310 311 // remaining size after subtracting the padding for the alignment 312 unsigned long r_size = (*c)->size - a_start + n_start; 313 // round down according to granularity 314 r_size &= ~(granularity - 1UL); 315 316 // block too small 317 if (r_size < min) 318 continue; 319 320 if (r_size >= *max) 321 { 322 fit = c; 323 max_fit = *max; 324 break; 325 } 326 327 if (r_size > max_fit) 328 { 329 max_fit = r_size; 330 fit = c; 331 } 332 } 333 334 if (fit) 335 { 336 unsigned long n_start = (unsigned long)(*fit); 337 unsigned long a_start = (n_start + almask) & ~almask; 338 unsigned long r_size = (*fit)->size - a_start + n_start; 339 340 if (a_start > n_start) 341 { 342 (*fit)->size -= r_size; 343 fit = &(*fit)->next; 344 } 345 else 346 *fit = (*fit)->next; 347 348 *max = max_fit; 349 if (r_size == max_fit) 350 return (void *)a_start; 351 352 Mem_block *m = (Mem_block*)(a_start + max_fit); 353 m->next = *fit; 354 m->size = r_size - max_fit; 355 *fit = m; 356 return (void *)a_start; 357 } 358 359 return 0; 360} 361 362void * 363List_alloc::alloc(unsigned long size, unsigned long align) 364{ 365 List_alloc_sanity_guard __attribute__((unused)) guard(this, __func__); 366 367 unsigned long const mb_align = (1UL << arith::Ld<sizeof(Mem_block)>::value) - 1; 368 369 // blow up size to the minimum aligned size 370 size = (size + mb_align) & ~mb_align; 371 372 unsigned long almask = align ? (align - 1UL) : 0; 373 374 // minimum alignment is given by the size of a Mem_block 375 if (almask < mb_align) 376 almask = mb_align; 377 378 Mem_block **c = &_first; 379 380 for (; *c; c=&(*c)->next) 381 { 382 // address of free memory block 383 unsigned long n_start = (unsigned long)(*c); 384 385 // block too small, next 386 // XXX: maybe we can skip this and just do the test below 387 if ((*c)->size < size) 388 continue; 389 390 // aligned start address within the free block 391 unsigned long a_start = (n_start + almask) & ~almask; 392 393 // block too small after alignment, next 394 if (a_start - n_start >= (*c)->size) 395 continue; 396 397 // remaining size after subtracting the padding 398 // for the alignment 399 unsigned long r_size = (*c)->size - a_start + n_start; 400 401 // block too small 402 if (r_size < size) 403 continue; 404 405 if (a_start > n_start) 406 { 407 // have free space before the allocated block 408 // shrink the block and set c to the next pointer of that 409 // block 410 (*c)->size -= r_size; 411 c = &(*c)->next; 412 } 413 else 414 // drop the block, c remains the next pointer of the 415 // previous block 416 *c = (*c)->next; 417 418 // allocated the whole remaining space 419 if (r_size == size) 420 return (void*)a_start; 421 422 // add a new free block behind the allocated block 423 Mem_block *m = (Mem_block*)(a_start + size); 424 m->next = *c; 425 m->size = r_size - size; 426 *c = m; 427 return (void *)a_start; 428 } 429 430 return 0; 431} 432 433unsigned long 434List_alloc::avail() 435{ 436 List_alloc_sanity_guard __attribute__((unused)) guard(this, __FUNCTION__); 437 Mem_block *c = _first; 438 unsigned long a = 0; 439 while (c) 440 { 441 a += c->size; 442 c = c->next; 443 } 444 445 return a; 446} 447 448template <typename DBG> 449void 450List_alloc::dump_free_list(DBG &out) 451{ 452 Mem_block *c = _first; 453 while (c) 454 { 455 unsigned sz; 456 const char *unit; 457 458 if (c->size < 1024) 459 { 460 sz = c->size; 461 unit = "Byte"; 462 } 463 else if (c->size < 1 << 20) 464 { 465 sz = c->size >> 10; 466 unit = "kB"; 467 } 468 else 469 { 470 sz = c->size >> 20; 471 unit = "MB"; 472 } 473 474 out.printf("%12p - %12p (%u %s)\n", c, (char *) c + c->size - 1, sz, unit); 475 476 c = c->next; 477 } 478} 479 480} 481