1 /* 2 * Copyright 2008-2009, Michael Lotz, mmlr@mlotz.ch. 3 * Distributed under the terms of the MIT License. 4 * 5 * Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. 6 * Distributed under the terms of the MIT License. 7 * 8 * Copyright 2001, Travis Geiselbrecht. All rights reserved. 9 * Distributed under the terms of the NewOS License. 10 */ 11 12 #include <arch/debug.h> 13 #include <debug.h> 14 #include <elf.h> 15 #include <heap.h> 16 #include <int.h> 17 #include <kernel.h> 18 #include <lock.h> 19 #include <malloc.h> 20 #include <signal.h> 21 #include <string.h> 22 #include <team.h> 23 #include <thread.h> 24 #include <tracing.h> 25 #include <util/AutoLock.h> 26 #include <vm.h> 27 28 29 //#define TRACE_HEAP 30 #ifdef TRACE_HEAP 31 # define TRACE(x) dprintf x 32 #else 33 # define TRACE(x) ; 34 #endif 35 36 37 #if KERNEL_HEAP_LEAK_CHECK 38 typedef struct heap_leak_check_info_s { 39 addr_t caller; 40 size_t size; 41 thread_id thread; 42 team_id team; 43 } heap_leak_check_info; 44 45 struct caller_info { 46 addr_t caller; 47 uint32 count; 48 uint32 size; 49 }; 50 51 static const int32 kCallerInfoTableSize = 1024; 52 static caller_info sCallerInfoTable[kCallerInfoTableSize]; 53 static int32 sCallerInfoCount = 0; 54 #endif 55 56 typedef struct heap_page_s heap_page; 57 58 typedef struct heap_area_s { 59 area_id area; 60 61 addr_t base; 62 size_t size; 63 64 uint32 page_count; 65 uint32 free_page_count; 66 67 heap_page * free_pages; 68 heap_page * page_table; 69 70 heap_area_s * prev; 71 heap_area_s * next; 72 heap_area_s * all_next; 73 } heap_area; 74 75 typedef struct heap_page_s { 76 heap_area * area; 77 uint16 index; 78 uint16 bin_index : 5; 79 uint16 free_count : 10; 80 uint16 in_use : 1; 81 heap_page_s * next; 82 heap_page_s * prev; 83 union { 84 uint16 empty_index; 85 uint16 allocation_id; // used for bin == bin_count allocations 86 }; 87 addr_t * free_list; 88 } heap_page; 89 90 typedef struct heap_bin_s { 91 mutex lock; 92 uint32 element_size; 93 uint16 max_free_count; 94 heap_page * page_list; // sorted so that the desired page is always first 95 } heap_bin; 96 97 struct heap_allocator_s { 98 rw_lock area_lock; 99 mutex page_lock; 100 101 const char *name; 102 uint32 bin_count; 103 uint32 page_size; 104 105 uint32 total_pages; 106 uint32 total_free_pages; 107 uint32 empty_areas; 108 109 #if KERNEL_HEAP_LEAK_CHECK 110 addr_t (*get_caller)(); 111 #endif 112 113 heap_bin * bins; 114 heap_area * areas; // sorted so that the desired area is always first 115 heap_area * all_areas; // all areas including full ones 116 }; 117 118 static const uint32 kAreaAllocationMagic = 'AAMG'; 119 typedef struct area_allocation_info_s { 120 area_id area; 121 void * base; 122 uint32 magic; 123 size_t size; 124 size_t allocation_size; 125 size_t allocation_alignment; 126 void * allocation_base; 127 } area_allocation_info; 128 129 struct DeferredFreeListEntry : SinglyLinkedListLinkImpl<DeferredFreeListEntry> { 130 }; 131 132 typedef SinglyLinkedList<DeferredFreeListEntry> DeferredFreeList; 133 typedef SinglyLinkedList<DeferredDeletable> DeferredDeletableList; 134 135 // Heap class configuration 136 #define HEAP_CLASS_COUNT 3 137 static heap_class sHeapClasses[HEAP_CLASS_COUNT] = { 138 { 139 "small", /* name */ 140 50, /* initial percentage */ 141 B_PAGE_SIZE / 8, /* max allocation size */ 142 B_PAGE_SIZE, /* page size */ 143 8, /* min bin size */ 144 4, /* bin alignment */ 145 8, /* min count per page */ 146 16 /* max waste per page */ 147 }, 148 { 149 "medium", /* name */ 150 30, /* initial percentage */ 151 B_PAGE_SIZE * 2, /* max allocation size */ 152 B_PAGE_SIZE * 8, /* page size */ 153 B_PAGE_SIZE / 8, /* min bin size */ 154 32, /* bin alignment */ 155 4, /* min count per page */ 156 64 /* max waste per page */ 157 }, 158 { 159 "large", /* name */ 160 20, /* initial percentage */ 161 HEAP_AREA_USE_THRESHOLD, /* max allocation size */ 162 B_PAGE_SIZE * 16, /* page size */ 163 B_PAGE_SIZE * 2, /* min bin size */ 164 128, /* bin alignment */ 165 1, /* min count per page */ 166 256 /* max waste per page */ 167 } 168 }; 169 170 static heap_allocator *sHeaps[HEAP_CLASS_COUNT]; 171 static uint32 *sLastGrowRequest[HEAP_CLASS_COUNT]; 172 static uint32 *sLastHandledGrowRequest[HEAP_CLASS_COUNT]; 173 static heap_allocator *sGrowHeap = NULL; 174 static thread_id sHeapGrowThread = -1; 175 static sem_id sHeapGrowSem = -1; 176 static sem_id sHeapGrownNotify = -1; 177 static bool sAddGrowHeap = false; 178 179 static DeferredFreeList sDeferredFreeList; 180 static DeferredDeletableList sDeferredDeletableList; 181 static spinlock sDeferredFreeListLock; 182 183 184 185 // #pragma mark - Tracing 186 187 #if KERNEL_HEAP_TRACING 188 namespace KernelHeapTracing { 189 190 class Allocate : public AbstractTraceEntry { 191 public: 192 Allocate(addr_t address, size_t size) 193 : fAddress(address), 194 fSize(size) 195 { 196 Initialized(); 197 } 198 199 virtual void AddDump(TraceOutput &out) 200 { 201 out.Print("heap allocate: 0x%08lx (%lu bytes)", fAddress, fSize); 202 } 203 204 private: 205 addr_t fAddress; 206 size_t fSize; 207 }; 208 209 210 class Reallocate : public AbstractTraceEntry { 211 public: 212 Reallocate(addr_t oldAddress, addr_t newAddress, size_t newSize) 213 : fOldAddress(oldAddress), 214 fNewAddress(newAddress), 215 fNewSize(newSize) 216 { 217 Initialized(); 218 }; 219 220 virtual void AddDump(TraceOutput &out) 221 { 222 out.Print("heap reallocate: 0x%08lx -> 0x%08lx (%lu bytes)", 223 fOldAddress, fNewAddress, fNewSize); 224 } 225 226 private: 227 addr_t fOldAddress; 228 addr_t fNewAddress; 229 size_t fNewSize; 230 }; 231 232 233 class Free : public AbstractTraceEntry { 234 public: 235 Free(addr_t address) 236 : fAddress(address) 237 { 238 Initialized(); 239 }; 240 241 virtual void AddDump(TraceOutput &out) 242 { 243 out.Print("heap free: 0x%08lx", fAddress); 244 } 245 246 private: 247 addr_t fAddress; 248 }; 249 250 251 } // namespace KernelHeapTracing 252 253 # define T(x) if (!gKernelStartup) new(std::nothrow) KernelHeapTracing::x; 254 #else 255 # define T(x) ; 256 #endif 257 258 259 // #pragma mark - Debug functions 260 261 262 #if KERNEL_HEAP_LEAK_CHECK 263 static addr_t 264 get_caller() 265 { 266 // Find the first return address outside of the allocator code. Note, that 267 // this makes certain assumptions about how the code for the functions 268 // ends up in the kernel object. 269 addr_t returnAddresses[5]; 270 int32 depth = arch_debug_get_stack_trace(returnAddresses, 5, 0, 1, false); 271 for (int32 i = 0; i < depth; i++) { 272 if (returnAddresses[i] < (addr_t)&get_caller 273 || returnAddresses[i] > (addr_t)&malloc_referenced_release) { 274 return returnAddresses[i]; 275 } 276 } 277 278 return 0; 279 } 280 #endif 281 282 283 static void 284 dump_page(heap_page *page) 285 { 286 uint32 count = 0; 287 for (addr_t *temp = page->free_list; temp != NULL; temp = (addr_t *)*temp) 288 count++; 289 290 kprintf("\t\tpage %p: bin_index: %u; free_count: %u; empty_index: %u; " 291 "free_list %p (%lu entr%s)\n", page, page->bin_index, page->free_count, 292 page->empty_index, page->free_list, count, count == 1 ? "y" : "ies"); 293 } 294 295 296 static void 297 dump_bin(heap_bin *bin) 298 { 299 kprintf("\telement_size: %lu; max_free_count: %u; page_list %p;\n", 300 bin->element_size, bin->max_free_count, bin->page_list); 301 302 for (heap_page *temp = bin->page_list; temp != NULL; temp = temp->next) 303 dump_page(temp); 304 } 305 306 307 static void 308 dump_bin_list(heap_allocator *heap) 309 { 310 for (uint32 i = 0; i < heap->bin_count; i++) 311 dump_bin(&heap->bins[i]); 312 kprintf("\n"); 313 } 314 315 316 static void 317 dump_allocator_areas(heap_allocator *heap) 318 { 319 heap_area *area = heap->all_areas; 320 while (area) { 321 kprintf("\tarea %p: area: %ld; base: 0x%08lx; size: %lu; page_count: " 322 "%lu; free_pages: %p (%lu entr%s)\n", area, area->area, area->base, 323 area->size, area->page_count, area->free_pages, 324 area->free_page_count, area->free_page_count == 1 ? "y" : "ies"); 325 area = area->all_next; 326 } 327 328 kprintf("\n"); 329 } 330 331 332 static void 333 dump_allocator(heap_allocator *heap, bool areas, bool bins) 334 { 335 kprintf("allocator %p: name: %s; page_size: %lu; bin_count: %lu; pages: " 336 "%lu; free_pages: %lu; empty_areas: %lu\n", heap, heap->name, 337 heap->page_size, heap->bin_count, heap->total_pages, 338 heap->total_free_pages, heap->empty_areas); 339 340 if (areas) 341 dump_allocator_areas(heap); 342 if (bins) 343 dump_bin_list(heap); 344 } 345 346 347 static int 348 dump_heap_list(int argc, char **argv) 349 { 350 if (argc == 2) { 351 uint64 heapAddress; 352 353 if (strcmp(argv[1], "grow") == 0) { 354 // only dump dedicated grow heap info 355 kprintf("dedicated grow heap:\n"); 356 dump_allocator(sGrowHeap, true, true); 357 } else if (strcmp(argv[1], "stats") == 0) { 358 for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) 359 dump_allocator(sHeaps[i], false, false); 360 } else if (evaluate_debug_expression(argv[1], &heapAddress, true)) { 361 dump_allocator((heap_allocator*)(addr_t)heapAddress, true, true); 362 } else 363 print_debugger_command_usage(argv[0]); 364 365 return 0; 366 } 367 368 for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) 369 dump_allocator(sHeaps[i], true, true); 370 371 return 0; 372 } 373 374 375 #if !KERNEL_HEAP_LEAK_CHECK 376 377 static int 378 dump_allocations(int argc, char **argv) 379 { 380 uint64 heapAddress = 0; 381 bool statsOnly = false; 382 for (int32 i = 1; i < argc; i++) { 383 if (strcmp(argv[i], "stats") == 0) 384 statsOnly = true; 385 else if (!evaluate_debug_expression(argv[i], &heapAddress, true)) { 386 print_debugger_command_usage(argv[0]); 387 return 0; 388 } 389 } 390 391 size_t totalSize = 0; 392 uint32 totalCount = 0; 393 for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT; classIndex++) { 394 heap_allocator *heap = sHeaps[classIndex]; 395 if (heapAddress != 0) 396 heap = (heap_allocator *)(addr_t)heapAddress; 397 398 // go through all the pages in all the areas 399 heap_area *area = heap->all_areas; 400 while (area) { 401 for (uint32 i = 0; i < area->page_count; i++) { 402 heap_page *page = &area->page_table[i]; 403 if (!page->in_use) 404 continue; 405 406 addr_t base = area->base + i * heap->page_size; 407 if (page->bin_index < heap->bin_count) { 408 // page is used by a small allocation bin 409 uint32 elementCount = page->empty_index; 410 size_t elementSize 411 = heap->bins[page->bin_index].element_size; 412 for (uint32 j = 0; j < elementCount; 413 j++, base += elementSize) { 414 // walk the free list to see if this element is in use 415 bool elementInUse = true; 416 for (addr_t *temp = page->free_list; temp != NULL; 417 temp = (addr_t *)*temp) { 418 if ((addr_t)temp == base) { 419 elementInUse = false; 420 break; 421 } 422 } 423 424 if (!elementInUse) 425 continue; 426 427 if (!statsOnly) { 428 kprintf("address: 0x%08lx; size: %lu bytes\n", 429 base, elementSize); 430 } 431 432 totalSize += elementSize; 433 totalCount++; 434 } 435 } else { 436 // page is used by a big allocation, find the page count 437 uint32 pageCount = 1; 438 while (i + pageCount < area->page_count 439 && area->page_table[i + pageCount].in_use 440 && area->page_table[i + pageCount].bin_index 441 == heap->bin_count 442 && area->page_table[i + pageCount].allocation_id 443 == page->allocation_id) 444 pageCount++; 445 446 size_t size = pageCount * heap->page_size; 447 448 if (!statsOnly) { 449 kprintf("address: 0x%08lx; size: %lu bytes\n", base, 450 size); 451 } 452 453 totalSize += size; 454 totalCount++; 455 456 // skip the allocated pages 457 i += pageCount - 1; 458 } 459 } 460 461 area = area->all_next; 462 } 463 464 if (heapAddress != 0) 465 break; 466 } 467 468 kprintf("total allocations: %lu; total bytes: %lu\n", totalCount, totalSize); 469 return 0; 470 } 471 472 #else // !KERNEL_HEAP_LEAK_CHECK 473 474 static int 475 dump_allocations(int argc, char **argv) 476 { 477 team_id team = -1; 478 thread_id thread = -1; 479 addr_t caller = 0; 480 bool statsOnly = false; 481 for (int32 i = 1; i < argc; i++) { 482 if (strcmp(argv[i], "team") == 0) 483 team = strtoul(argv[++i], NULL, 0); 484 else if (strcmp(argv[i], "thread") == 0) 485 thread = strtoul(argv[++i], NULL, 0); 486 else if (strcmp(argv[i], "caller") == 0) 487 caller = strtoul(argv[++i], NULL, 0); 488 else if (strcmp(argv[i], "stats") == 0) 489 statsOnly = true; 490 else { 491 print_debugger_command_usage(argv[0]); 492 return 0; 493 } 494 } 495 496 size_t totalSize = 0; 497 uint32 totalCount = 0; 498 for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT; classIndex++) { 499 heap_allocator *heap = sHeaps[classIndex]; 500 501 // go through all the pages in all the areas 502 heap_area *area = heap->all_areas; 503 while (area) { 504 heap_leak_check_info *info = NULL; 505 for (uint32 i = 0; i < area->page_count; i++) { 506 heap_page *page = &area->page_table[i]; 507 if (!page->in_use) 508 continue; 509 510 addr_t base = area->base + i * heap->page_size; 511 if (page->bin_index < heap->bin_count) { 512 // page is used by a small allocation bin 513 uint32 elementCount = page->empty_index; 514 size_t elementSize 515 = heap->bins[page->bin_index].element_size; 516 for (uint32 j = 0; j < elementCount; 517 j++, base += elementSize) { 518 // walk the free list to see if this element is in use 519 bool elementInUse = true; 520 for (addr_t *temp = page->free_list; temp != NULL; 521 temp = (addr_t *)*temp) { 522 if ((addr_t)temp == base) { 523 elementInUse = false; 524 break; 525 } 526 } 527 528 if (!elementInUse) 529 continue; 530 531 info = (heap_leak_check_info *)(base + elementSize 532 - sizeof(heap_leak_check_info)); 533 534 if ((team == -1 || info->team == team) 535 && (thread == -1 || info->thread == thread) 536 && (caller == 0 || info->caller == caller)) { 537 // interesting... 538 if (!statsOnly) { 539 kprintf("team: % 6ld; thread: % 6ld; " 540 "address: 0x%08lx; size: %lu bytes; " 541 "caller: %#lx\n", info->team, info->thread, 542 base, info->size, info->caller); 543 } 544 545 totalSize += info->size; 546 totalCount++; 547 } 548 } 549 } else { 550 // page is used by a big allocation, find the page count 551 uint32 pageCount = 1; 552 while (i + pageCount < area->page_count 553 && area->page_table[i + pageCount].in_use 554 && area->page_table[i + pageCount].bin_index 555 == heap->bin_count 556 && area->page_table[i + pageCount].allocation_id 557 == page->allocation_id) 558 pageCount++; 559 560 info = (heap_leak_check_info *)(base + pageCount 561 * heap->page_size - sizeof(heap_leak_check_info)); 562 563 if ((team == -1 || info->team == team) 564 && (thread == -1 || info->thread == thread) 565 && (caller == 0 || info->caller == caller)) { 566 // interesting... 567 if (!statsOnly) { 568 kprintf("team: % 6ld; thread: % 6ld;" 569 " address: 0x%08lx; size: %lu bytes;" 570 " caller: %#lx\n", info->team, info->thread, 571 base, info->size, info->caller); 572 } 573 574 totalSize += info->size; 575 totalCount++; 576 } 577 578 // skip the allocated pages 579 i += pageCount - 1; 580 } 581 } 582 583 area = area->all_next; 584 } 585 } 586 587 kprintf("total allocations: %lu; total bytes: %lu\n", totalCount, totalSize); 588 return 0; 589 } 590 591 592 static caller_info* 593 get_caller_info(addr_t caller) 594 { 595 // find the caller info 596 for (int32 i = 0; i < sCallerInfoCount; i++) { 597 if (caller == sCallerInfoTable[i].caller) 598 return &sCallerInfoTable[i]; 599 } 600 601 // not found, add a new entry, if there are free slots 602 if (sCallerInfoCount >= kCallerInfoTableSize) 603 return NULL; 604 605 caller_info* info = &sCallerInfoTable[sCallerInfoCount++]; 606 info->caller = caller; 607 info->count = 0; 608 info->size = 0; 609 610 return info; 611 } 612 613 614 static int 615 caller_info_compare_size(const void* _a, const void* _b) 616 { 617 const caller_info* a = (const caller_info*)_a; 618 const caller_info* b = (const caller_info*)_b; 619 return (int)(b->size - a->size); 620 } 621 622 623 static int 624 caller_info_compare_count(const void* _a, const void* _b) 625 { 626 const caller_info* a = (const caller_info*)_a; 627 const caller_info* b = (const caller_info*)_b; 628 return (int)(b->count - a->count); 629 } 630 631 632 static bool 633 analyze_allocation_callers(heap_allocator *heap) 634 { 635 // go through all the pages in all the areas 636 heap_area *area = heap->all_areas; 637 while (area) { 638 heap_leak_check_info *info = NULL; 639 for (uint32 i = 0; i < area->page_count; i++) { 640 heap_page *page = &area->page_table[i]; 641 if (!page->in_use) 642 continue; 643 644 addr_t base = area->base + i * heap->page_size; 645 if (page->bin_index < heap->bin_count) { 646 // page is used by a small allocation bin 647 uint32 elementCount = page->empty_index; 648 size_t elementSize = heap->bins[page->bin_index].element_size; 649 for (uint32 j = 0; j < elementCount; j++, base += elementSize) { 650 // walk the free list to see if this element is in use 651 bool elementInUse = true; 652 for (addr_t *temp = page->free_list; temp != NULL; 653 temp = (addr_t *)*temp) { 654 if ((addr_t)temp == base) { 655 elementInUse = false; 656 break; 657 } 658 } 659 660 if (!elementInUse) 661 continue; 662 663 info = (heap_leak_check_info *)(base + elementSize 664 - sizeof(heap_leak_check_info)); 665 666 caller_info *callerInfo = get_caller_info(info->caller); 667 if (callerInfo == NULL) { 668 kprintf("out of space for caller infos\n"); 669 return 0; 670 } 671 672 callerInfo->count++; 673 callerInfo->size += info->size; 674 } 675 } else { 676 // page is used by a big allocation, find the page count 677 uint32 pageCount = 1; 678 while (i + pageCount < area->page_count 679 && area->page_table[i + pageCount].in_use 680 && area->page_table[i + pageCount].bin_index 681 == heap->bin_count 682 && area->page_table[i + pageCount].allocation_id 683 == page->allocation_id) { 684 pageCount++; 685 } 686 687 info = (heap_leak_check_info *)(base + pageCount 688 * heap->page_size - sizeof(heap_leak_check_info)); 689 690 caller_info *callerInfo = get_caller_info(info->caller); 691 if (callerInfo == NULL) { 692 kprintf("out of space for caller infos\n"); 693 return false; 694 } 695 696 callerInfo->count++; 697 callerInfo->size += info->size; 698 699 // skip the allocated pages 700 i += pageCount - 1; 701 } 702 } 703 704 area = area->all_next; 705 } 706 707 return true; 708 } 709 710 711 static int 712 dump_allocations_per_caller(int argc, char **argv) 713 { 714 bool sortBySize = true; 715 heap_allocator *heap = NULL; 716 717 for (int32 i = 1; i < argc; i++) { 718 if (strcmp(argv[i], "-c") == 0) { 719 sortBySize = false; 720 } else if (strcmp(argv[i], "-h") == 0) { 721 uint64 heapAddress; 722 if (++i >= argc 723 || !evaluate_debug_expression(argv[i], &heapAddress, true)) { 724 print_debugger_command_usage(argv[0]); 725 return 0; 726 } 727 728 heap = (heap_allocator*)(addr_t)heapAddress; 729 } else { 730 print_debugger_command_usage(argv[0]); 731 return 0; 732 } 733 } 734 735 sCallerInfoCount = 0; 736 737 if (heap != NULL) { 738 if (!analyze_allocation_callers(heap)) 739 return 0; 740 } else { 741 for (uint32 classIndex = 0; classIndex < HEAP_CLASS_COUNT; 742 classIndex++) { 743 if (!analyze_allocation_callers(sHeaps[classIndex])) 744 return 0; 745 } 746 } 747 748 // sort the array 749 qsort(sCallerInfoTable, sCallerInfoCount, sizeof(caller_info), 750 sortBySize ? &caller_info_compare_size : &caller_info_compare_count); 751 752 kprintf("%ld different callers, sorted by %s...\n\n", sCallerInfoCount, 753 sortBySize ? "size" : "count"); 754 755 kprintf(" count size caller\n"); 756 kprintf("----------------------------------\n"); 757 for (int32 i = 0; i < sCallerInfoCount; i++) { 758 caller_info& info = sCallerInfoTable[i]; 759 kprintf("%10ld %10ld %#08lx", info.count, info.size, info.caller); 760 761 const char *symbol; 762 const char *imageName; 763 bool exactMatch; 764 addr_t baseAddress; 765 766 if (elf_debug_lookup_symbol_address(info.caller, &baseAddress, &symbol, 767 &imageName, &exactMatch) == B_OK) { 768 kprintf(" %s + 0x%lx (%s)%s\n", symbol, 769 info.caller - baseAddress, imageName, 770 exactMatch ? "" : " (nearest)"); 771 } else 772 kprintf("\n"); 773 } 774 775 return 0; 776 } 777 778 #endif // KERNEL_HEAP_LEAK_CHECK 779 780 781 #if PARANOID_HEAP_VALIDATION 782 static void 783 heap_validate_heap(heap_allocator *heap) 784 { 785 ReadLocker areaReadLocker(heap->area_lock); 786 for (uint32 i = 0; i < heap->bin_count; i++) 787 mutex_lock(&heap->bins[i].lock); 788 MutexLocker pageLocker(heap->page_lock); 789 790 uint32 totalPageCount = 0; 791 uint32 totalFreePageCount = 0; 792 heap_area *area = heap->all_areas; 793 while (area != NULL) { 794 // validate the free pages list 795 uint32 freePageCount = 0; 796 heap_page *lastPage = NULL; 797 heap_page *page = area->free_pages; 798 while (page) { 799 if ((addr_t)page < (addr_t)&area->page_table[0] 800 || (addr_t)page >= (addr_t)&area->page_table[area->page_count]) 801 panic("free page is not part of the page table\n"); 802 803 if (page->index >= area->page_count) 804 panic("free page has invalid index\n"); 805 806 if ((addr_t)&area->page_table[page->index] != (addr_t)page) 807 panic("free page index does not lead to target page\n"); 808 809 if (page->prev != lastPage) 810 panic("free page entry has invalid prev link\n"); 811 812 if (page->in_use) 813 panic("free page marked as in use\n"); 814 815 lastPage = page; 816 page = page->next; 817 freePageCount++; 818 } 819 820 totalPageCount += freePageCount; 821 totalFreePageCount += freePageCount; 822 if (area->free_page_count != freePageCount) 823 panic("free page count doesn't match free page list\n"); 824 825 // validate the page table 826 uint32 usedPageCount = 0; 827 for (uint32 i = 0; i < area->page_count; i++) { 828 if (area->page_table[i].in_use) 829 usedPageCount++; 830 } 831 832 totalPageCount += usedPageCount; 833 if (freePageCount + usedPageCount != area->page_count) { 834 panic("free pages and used pages do not add up (%lu + %lu != %lu)\n", 835 freePageCount, usedPageCount, area->page_count); 836 } 837 838 area = area->all_next; 839 } 840 841 // validate the areas 842 area = heap->areas; 843 heap_area *lastArea = NULL; 844 uint32 lastFreeCount = 0; 845 while (area != NULL) { 846 if (area->free_page_count < lastFreeCount) 847 panic("size ordering of area list broken\n"); 848 849 if (area->prev != lastArea) 850 panic("area list entry has invalid prev link\n"); 851 852 lastArea = area; 853 lastFreeCount = area->free_page_count; 854 area = area->next; 855 } 856 857 lastArea = NULL; 858 area = heap->all_areas; 859 while (area != NULL) { 860 if (lastArea != NULL && lastArea->base < area->base) 861 panic("base ordering of all_areas list broken\n"); 862 863 lastArea = area; 864 area = area->all_next; 865 } 866 867 // validate the bins 868 for (uint32 i = 0; i < heap->bin_count; i++) { 869 heap_bin *bin = &heap->bins[i]; 870 heap_page *lastPage = NULL; 871 heap_page *page = bin->page_list; 872 lastFreeCount = 0; 873 while (page) { 874 area = heap->all_areas; 875 while (area) { 876 if (area == page->area) 877 break; 878 area = area->all_next; 879 } 880 881 if (area == NULL) { 882 panic("page area not present in area list\n"); 883 page = page->next; 884 continue; 885 } 886 887 if ((addr_t)page < (addr_t)&area->page_table[0] 888 || (addr_t)page >= (addr_t)&area->page_table[area->page_count]) 889 panic("used page is not part of the page table\n"); 890 891 if (page->index >= area->page_count) 892 panic("used page has invalid index\n"); 893 894 if ((addr_t)&area->page_table[page->index] != (addr_t)page) 895 panic("used page index does not lead to target page\n"); 896 897 if (page->prev != lastPage) { 898 panic("used page entry has invalid prev link (%p vs %p bin " 899 "%lu)\n", page->prev, lastPage, i); 900 } 901 902 if (!page->in_use) 903 panic("used page marked as not in use\n"); 904 905 if (page->bin_index != i) { 906 panic("used page with bin index %u in page list of bin %lu\n", 907 page->bin_index, i); 908 } 909 910 if (page->free_count < lastFreeCount) 911 panic("ordering of bin page list broken\n"); 912 913 // validate the free list 914 uint32 freeSlotsCount = 0; 915 addr_t *element = page->free_list; 916 addr_t pageBase = area->base + page->index * heap->page_size; 917 while (element) { 918 if ((addr_t)element < pageBase 919 || (addr_t)element >= pageBase + heap->page_size) 920 panic("free list entry out of page range\n"); 921 922 if (((addr_t)element - pageBase) % bin->element_size != 0) 923 panic("free list entry not on a element boundary\n"); 924 925 element = (addr_t *)*element; 926 freeSlotsCount++; 927 } 928 929 uint32 slotCount = bin->max_free_count; 930 if (page->empty_index > slotCount) { 931 panic("empty index beyond slot count (%u with %lu slots)\n", 932 page->empty_index, slotCount); 933 } 934 935 freeSlotsCount += (slotCount - page->empty_index); 936 if (freeSlotsCount > slotCount) 937 panic("more free slots than fit into the page\n"); 938 939 lastPage = page; 940 lastFreeCount = page->free_count; 941 page = page->next; 942 } 943 } 944 945 pageLocker.Unlock(); 946 for (uint32 i = 0; i < heap->bin_count; i++) 947 mutex_unlock(&heap->bins[i].lock); 948 areaReadLocker.Unlock(); 949 } 950 #endif // PARANOID_HEAP_VALIDATION 951 952 953 // #pragma mark - Heap functions 954 955 956 static void 957 heap_add_area(heap_allocator *heap, area_id areaID, addr_t base, size_t size) 958 { 959 heap_area *area = (heap_area *)base; 960 area->area = areaID; 961 962 base += sizeof(heap_area); 963 size -= sizeof(heap_area); 964 965 uint32 pageCount = size / heap->page_size; 966 size_t pageTableSize = pageCount * sizeof(heap_page); 967 area->page_table = (heap_page *)base; 968 base += pageTableSize; 969 size -= pageTableSize; 970 971 // the rest is now actually usable memory (rounded to the next page) 972 area->base = ROUNDUP(base, B_PAGE_SIZE); 973 area->size = size & ~(B_PAGE_SIZE - 1); 974 975 // now we know the real page count 976 pageCount = area->size / heap->page_size; 977 area->page_count = pageCount; 978 979 // zero out the page table and fill in page indexes 980 memset((void *)area->page_table, 0, pageTableSize); 981 for (uint32 i = 0; i < pageCount; i++) { 982 area->page_table[i].area = area; 983 area->page_table[i].index = i; 984 } 985 986 // add all pages up into the free pages list 987 for (uint32 i = 1; i < pageCount; i++) { 988 area->page_table[i - 1].next = &area->page_table[i]; 989 area->page_table[i].prev = &area->page_table[i - 1]; 990 } 991 area->free_pages = &area->page_table[0]; 992 area->free_page_count = pageCount; 993 area->page_table[0].prev = NULL; 994 area->next = NULL; 995 996 WriteLocker areaWriteLocker(heap->area_lock); 997 MutexLocker pageLocker(heap->page_lock); 998 if (heap->areas == NULL) { 999 // it's the only (empty) area in that heap 1000 area->prev = NULL; 1001 heap->areas = area; 1002 } else { 1003 // link in this area as the last one as it is completely empty 1004 heap_area *lastArea = heap->areas; 1005 while (lastArea->next != NULL) 1006 lastArea = lastArea->next; 1007 1008 lastArea->next = area; 1009 area->prev = lastArea; 1010 } 1011 1012 // insert this area in the all_areas list so it stays ordered by base 1013 if (heap->all_areas == NULL || heap->all_areas->base < area->base) { 1014 area->all_next = heap->all_areas; 1015 heap->all_areas = area; 1016 } else { 1017 heap_area *insert = heap->all_areas; 1018 while (insert->all_next && insert->all_next->base > area->base) 1019 insert = insert->all_next; 1020 1021 area->all_next = insert->all_next; 1022 insert->all_next = area; 1023 } 1024 1025 heap->total_pages += area->page_count; 1026 heap->total_free_pages += area->free_page_count; 1027 1028 if (areaID >= 0) { 1029 // this later on deletable area is yet empty - the empty count will be 1030 // decremented as soon as this area is used for the first time 1031 heap->empty_areas++; 1032 } 1033 1034 pageLocker.Unlock(); 1035 areaWriteLocker.Unlock(); 1036 1037 dprintf("heap_add_area: area %ld added to %s heap %p - usable range 0x%08lx " 1038 "- 0x%08lx\n", area->area, heap->name, heap, area->base, 1039 area->base + area->size); 1040 } 1041 1042 1043 static status_t 1044 heap_remove_area(heap_allocator *heap, heap_area *area) 1045 { 1046 if (area->free_page_count != area->page_count) { 1047 panic("tried removing heap area that has still pages in use"); 1048 return B_ERROR; 1049 } 1050 1051 if (area->prev == NULL && area->next == NULL) { 1052 panic("tried removing the last non-full heap area"); 1053 return B_ERROR; 1054 } 1055 1056 if (heap->areas == area) 1057 heap->areas = area->next; 1058 if (area->prev != NULL) 1059 area->prev->next = area->next; 1060 if (area->next != NULL) 1061 area->next->prev = area->prev; 1062 1063 if (heap->all_areas == area) 1064 heap->all_areas = area->all_next; 1065 else { 1066 heap_area *previous = heap->all_areas; 1067 while (previous) { 1068 if (previous->all_next == area) { 1069 previous->all_next = area->all_next; 1070 break; 1071 } 1072 1073 previous = previous->all_next; 1074 } 1075 1076 if (previous == NULL) 1077 panic("removing heap area that is not in all list"); 1078 } 1079 1080 heap->total_pages -= area->page_count; 1081 heap->total_free_pages -= area->free_page_count; 1082 1083 dprintf("heap_remove_area: area %ld with range 0x%08lx - 0x%08lx removed " 1084 "from %s heap %p\n", area->area, area->base, area->base + area->size, 1085 heap->name, heap); 1086 1087 return B_OK; 1088 } 1089 1090 1091 heap_allocator * 1092 heap_create_allocator(const char *name, addr_t base, size_t size, 1093 const heap_class *heapClass) 1094 { 1095 heap_allocator *heap = (heap_allocator *)base; 1096 base += sizeof(heap_allocator); 1097 size -= sizeof(heap_allocator); 1098 1099 heap->name = name; 1100 heap->page_size = heapClass->page_size; 1101 heap->total_pages = heap->total_free_pages = heap->empty_areas = 0; 1102 heap->areas = heap->all_areas = NULL; 1103 heap->bins = (heap_bin *)base; 1104 1105 #if KERNEL_HEAP_LEAK_CHECK 1106 heap->get_caller = &get_caller; 1107 #endif 1108 1109 heap->bin_count = 0; 1110 size_t binSize = 0, lastSize = 0; 1111 uint32 count = heap->page_size / heapClass->min_bin_size; 1112 for (; count >= heapClass->min_count_per_page; count--, lastSize = binSize) { 1113 binSize = (heap->page_size / count) & ~(heapClass->bin_alignment - 1); 1114 if (binSize == lastSize) 1115 continue; 1116 if (heap->page_size - count * binSize > heapClass->max_waste_per_page) 1117 continue; 1118 1119 heap_bin *bin = &heap->bins[heap->bin_count]; 1120 mutex_init(&bin->lock, "heap bin lock"); 1121 bin->element_size = binSize; 1122 bin->max_free_count = heap->page_size / binSize; 1123 bin->page_list = NULL; 1124 heap->bin_count++; 1125 1126 if (heap->bin_count >= 32) 1127 panic("heap configuration invalid - max bin count reached\n"); 1128 }; 1129 1130 base += heap->bin_count * sizeof(heap_bin); 1131 size -= heap->bin_count * sizeof(heap_bin); 1132 1133 rw_lock_init(&heap->area_lock, "heap area rw lock"); 1134 mutex_init(&heap->page_lock, "heap page lock"); 1135 1136 heap_add_area(heap, -1, base, size); 1137 return heap; 1138 } 1139 1140 1141 static inline void 1142 heap_free_pages_added(heap_allocator *heap, heap_area *area, uint32 pageCount) 1143 { 1144 area->free_page_count += pageCount; 1145 heap->total_free_pages += pageCount; 1146 1147 if (area->free_page_count == pageCount) { 1148 // we need to add ourselfs to the area list of the heap 1149 area->prev = NULL; 1150 area->next = heap->areas; 1151 if (area->next) 1152 area->next->prev = area; 1153 heap->areas = area; 1154 } else { 1155 // we might need to move back in the area list 1156 if (area->next && area->next->free_page_count < area->free_page_count) { 1157 // move ourselfs so the list stays ordered 1158 heap_area *insert = area->next; 1159 while (insert->next 1160 && insert->next->free_page_count < area->free_page_count) 1161 insert = insert->next; 1162 1163 if (area->prev) 1164 area->prev->next = area->next; 1165 if (area->next) 1166 area->next->prev = area->prev; 1167 if (heap->areas == area) 1168 heap->areas = area->next; 1169 1170 area->prev = insert; 1171 area->next = insert->next; 1172 if (area->next) 1173 area->next->prev = area; 1174 insert->next = area; 1175 } 1176 } 1177 1178 if (area->free_page_count == area->page_count && area->area >= 0) 1179 heap->empty_areas++; 1180 } 1181 1182 1183 static inline void 1184 heap_free_pages_removed(heap_allocator *heap, heap_area *area, uint32 pageCount) 1185 { 1186 if (area->free_page_count == area->page_count && area->area >= 0) { 1187 // this area was completely empty 1188 heap->empty_areas--; 1189 } 1190 1191 area->free_page_count -= pageCount; 1192 heap->total_free_pages -= pageCount; 1193 1194 if (area->free_page_count == 0) { 1195 // the area is now full so we remove it from the area list 1196 if (area->prev) 1197 area->prev->next = area->next; 1198 if (area->next) 1199 area->next->prev = area->prev; 1200 if (heap->areas == area) 1201 heap->areas = area->next; 1202 area->next = area->prev = NULL; 1203 } else { 1204 // we might need to move forward in the area list 1205 if (area->prev && area->prev->free_page_count > area->free_page_count) { 1206 // move ourselfs so the list stays ordered 1207 heap_area *insert = area->prev; 1208 while (insert->prev 1209 && insert->prev->free_page_count > area->free_page_count) 1210 insert = insert->prev; 1211 1212 if (area->prev) 1213 area->prev->next = area->next; 1214 if (area->next) 1215 area->next->prev = area->prev; 1216 1217 area->prev = insert->prev; 1218 area->next = insert; 1219 if (area->prev) 1220 area->prev->next = area; 1221 if (heap->areas == insert) 1222 heap->areas = area; 1223 insert->prev = area; 1224 } 1225 } 1226 } 1227 1228 1229 static inline void 1230 heap_link_page(heap_page *page, heap_page **list) 1231 { 1232 page->prev = NULL; 1233 page->next = *list; 1234 if (page->next) 1235 page->next->prev = page; 1236 *list = page; 1237 } 1238 1239 1240 static inline void 1241 heap_unlink_page(heap_page *page, heap_page **list) 1242 { 1243 if (page->prev) 1244 page->prev->next = page->next; 1245 if (page->next) 1246 page->next->prev = page->prev; 1247 if (list && *list == page) { 1248 *list = page->next; 1249 if (page->next) 1250 page->next->prev = NULL; 1251 } 1252 } 1253 1254 1255 static heap_page * 1256 heap_allocate_contiguous_pages(heap_allocator *heap, uint32 pageCount) 1257 { 1258 MutexLocker pageLocker(heap->page_lock); 1259 heap_area *area = heap->areas; 1260 while (area) { 1261 bool found = false; 1262 int32 first = -1; 1263 for (uint32 i = 0; i < area->page_count; i++) { 1264 if (area->page_table[i].in_use) { 1265 first = -1; 1266 continue; 1267 } 1268 1269 if (first > 0) { 1270 if ((i + 1 - first) == pageCount) { 1271 found = true; 1272 break; 1273 } 1274 } else 1275 first = i; 1276 } 1277 1278 if (!found) { 1279 area = area->next; 1280 continue; 1281 } 1282 1283 for (uint32 i = first; i < first + pageCount; i++) { 1284 heap_page *page = &area->page_table[i]; 1285 page->in_use = 1; 1286 page->bin_index = heap->bin_count; 1287 1288 heap_unlink_page(page, &area->free_pages); 1289 1290 page->next = page->prev = NULL; 1291 page->free_list = NULL; 1292 page->allocation_id = (uint16)first; 1293 } 1294 1295 heap_free_pages_removed(heap, area, pageCount); 1296 return &area->page_table[first]; 1297 } 1298 1299 return NULL; 1300 } 1301 1302 1303 static void * 1304 heap_raw_alloc(heap_allocator *heap, size_t size, uint32 binIndex) 1305 { 1306 TRACE(("raw_alloc: heap %p; size %lu; bin %lu\n", heap, size, binIndex)); 1307 if (binIndex < heap->bin_count) { 1308 heap_bin *bin = &heap->bins[binIndex]; 1309 MutexLocker binLocker(bin->lock); 1310 1311 heap_page *page = bin->page_list; 1312 if (page == NULL) { 1313 MutexLocker pageLocker(heap->page_lock); 1314 heap_area *area = heap->areas; 1315 if (area == NULL) { 1316 TRACE(("heap %p: no free pages to allocate %lu bytes\n", heap, 1317 size)); 1318 return NULL; 1319 } 1320 1321 // by design there are only areas in the list that still have 1322 // free pages available 1323 page = area->free_pages; 1324 area->free_pages = page->next; 1325 if (page->next) 1326 page->next->prev = NULL; 1327 1328 heap_free_pages_removed(heap, area, 1); 1329 1330 if (page->in_use) 1331 panic("got an in use page from the free pages list\n"); 1332 page->in_use = 1; 1333 1334 pageLocker.Unlock(); 1335 1336 page->bin_index = binIndex; 1337 page->free_count = bin->max_free_count; 1338 page->empty_index = 0; 1339 page->free_list = NULL; 1340 page->next = page->prev = NULL; 1341 bin->page_list = page; 1342 } 1343 1344 // we have a page where we have a free slot 1345 void *address = NULL; 1346 if (page->free_list) { 1347 // there's a previously freed entry we can use 1348 address = page->free_list; 1349 page->free_list = (addr_t *)*page->free_list; 1350 } else { 1351 // the page hasn't been fully allocated so use the next empty_index 1352 address = (void *)(page->area->base + page->index * heap->page_size 1353 + page->empty_index * bin->element_size); 1354 page->empty_index++; 1355 } 1356 1357 page->free_count--; 1358 if (page->free_count == 0) { 1359 // the page is now full so we remove it from the page_list 1360 bin->page_list = page->next; 1361 if (page->next) 1362 page->next->prev = NULL; 1363 page->next = page->prev = NULL; 1364 } 1365 1366 binLocker.Unlock(); 1367 1368 #if KERNEL_HEAP_LEAK_CHECK 1369 heap_leak_check_info *info = (heap_leak_check_info *)((addr_t)address 1370 + bin->element_size - sizeof(heap_leak_check_info)); 1371 info->size = size - sizeof(heap_leak_check_info); 1372 info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id()); 1373 info->team = (gKernelStartup ? 0 : team_get_current_team_id()); 1374 info->caller = heap->get_caller(); 1375 #endif 1376 return address; 1377 } 1378 1379 uint32 pageCount = (size + heap->page_size - 1) / heap->page_size; 1380 heap_page *firstPage = heap_allocate_contiguous_pages(heap, pageCount); 1381 if (firstPage == NULL) { 1382 TRACE(("heap %p: found no contiguous pages to allocate %ld bytes\n", 1383 heap, size)); 1384 return NULL; 1385 } 1386 1387 #if KERNEL_HEAP_LEAK_CHECK 1388 heap_leak_check_info *info = (heap_leak_check_info *)(firstPage->area->base 1389 + (firstPage->index + pageCount) * heap->page_size 1390 - sizeof(heap_leak_check_info)); 1391 info->size = size - sizeof(heap_leak_check_info); 1392 info->thread = (gKernelStartup ? 0 : thread_get_current_thread_id()); 1393 info->team = (gKernelStartup ? 0 : team_get_current_team_id()); 1394 info->caller = heap->get_caller(); 1395 #endif 1396 return (void *)(firstPage->area->base + firstPage->index * heap->page_size); 1397 } 1398 1399 1400 #if DEBUG 1401 static bool 1402 is_valid_alignment(size_t number) 1403 { 1404 // this cryptic line accepts zero and all powers of two 1405 return ((~number + 1) | ((number << 1) - 1)) == ~0UL; 1406 } 1407 #endif 1408 1409 1410 inline bool 1411 heap_should_grow(heap_allocator *heap) 1412 { 1413 // suggest growing if there is less than 20% of a grow size available 1414 return heap->total_free_pages * heap->page_size < HEAP_GROW_SIZE / 5; 1415 } 1416 1417 1418 void * 1419 heap_memalign(heap_allocator *heap, size_t alignment, size_t size) 1420 { 1421 TRACE(("memalign(alignment = %lu, size = %lu)\n", alignment, size)); 1422 1423 #if DEBUG 1424 if (!is_valid_alignment(alignment)) 1425 panic("memalign() with an alignment which is not a power of 2\n"); 1426 #endif 1427 1428 #if KERNEL_HEAP_LEAK_CHECK 1429 size += sizeof(heap_leak_check_info); 1430 #endif 1431 1432 if (alignment != 0) { 1433 // TODO: The alignment is done by ensuring that the element size 1434 // of the target bin is aligned with the requested alignment. This 1435 // has the problem that it wastes space because a better (smaller) bin 1436 // could possibly be selected. We should pick the best bin and check 1437 // if there is an aligned block in the free list or if a new (page 1438 // aligned) page has to be allocated anyway. 1439 size = ROUNDUP(size, alignment); 1440 } 1441 1442 uint32 binIndex; 1443 for (binIndex = 0; binIndex < heap->bin_count; binIndex++) { 1444 if (size <= heap->bins[binIndex].element_size) 1445 break; 1446 } 1447 1448 void *address = heap_raw_alloc(heap, size, binIndex); 1449 1450 #if KERNEL_HEAP_LEAK_CHECK 1451 size -= sizeof(heap_leak_check_info); 1452 #endif 1453 1454 TRACE(("memalign(): asked to allocate %lu bytes, returning pointer %p\n", 1455 size, address)); 1456 1457 T(Allocate((addr_t)address, size)); 1458 if (address == NULL) 1459 return address; 1460 1461 #if PARANOID_KERNEL_MALLOC 1462 memset(address, 0xcc, size); 1463 #endif 1464 1465 #if PARANOID_KERNEL_FREE 1466 // make sure 0xdeadbeef is cleared if we do not overwrite the memory 1467 // and the user does not clear it 1468 if (((uint32 *)address)[1] == 0xdeadbeef) 1469 ((uint32 *)address)[1] = 0xcccccccc; 1470 #endif 1471 1472 return address; 1473 } 1474 1475 1476 status_t 1477 heap_free(heap_allocator *heap, void *address) 1478 { 1479 if (address == NULL) 1480 return B_OK; 1481 1482 ReadLocker areaReadLocker(heap->area_lock); 1483 heap_area *area = heap->all_areas; 1484 while (area) { 1485 // since the all_areas list is ordered by base with the biggest 1486 // base at the top, we need only find the first area with a base 1487 // smaller than our address to become our only candidate for freeing 1488 if (area->base <= (addr_t)address) { 1489 if ((addr_t)address >= area->base + area->size) { 1490 // none of the other areas can contain the address as the list 1491 // is ordered 1492 return B_ENTRY_NOT_FOUND; 1493 } 1494 1495 // this area contains the allocation, we're done searching 1496 break; 1497 } 1498 1499 area = area->all_next; 1500 } 1501 1502 if (area == NULL) { 1503 // this address does not belong to us 1504 return B_ENTRY_NOT_FOUND; 1505 } 1506 1507 TRACE(("free(): asked to free pointer %p\n", address)); 1508 1509 heap_page *page = &area->page_table[((addr_t)address - area->base) 1510 / heap->page_size]; 1511 1512 TRACE(("free(): page %p: bin_index %d, free_count %d\n", page, 1513 page->bin_index, page->free_count)); 1514 1515 if (page->bin_index > heap->bin_count) { 1516 panic("free(): page %p: invalid bin_index %d\n", page, page->bin_index); 1517 return B_ERROR; 1518 } 1519 1520 if (page->bin_index < heap->bin_count) { 1521 // small allocation 1522 heap_bin *bin = &heap->bins[page->bin_index]; 1523 1524 #if PARANOID_KERNEL_FREE 1525 if (((uint32 *)address)[1] == 0xdeadbeef) { 1526 // This block looks like it was freed already, walk the free list 1527 // on this page to make sure this address doesn't exist. 1528 MutexLocker binLocker(bin->lock); 1529 for (addr_t *temp = page->free_list; temp != NULL; 1530 temp = (addr_t *)*temp) { 1531 if (temp == address) { 1532 panic("free(): address %p already exists in page free " 1533 "list\n", address); 1534 return B_ERROR; 1535 } 1536 } 1537 } 1538 1539 // the first 4 bytes are overwritten with the next free list pointer 1540 // later 1541 uint32 *dead = (uint32 *)address; 1542 for (uint32 i = 1; i < bin->element_size / sizeof(uint32); i++) 1543 dead[i] = 0xdeadbeef; 1544 #endif 1545 1546 MutexLocker binLocker(bin->lock); 1547 if (((addr_t)address - area->base - page->index 1548 * heap->page_size) % bin->element_size != 0) { 1549 panic("free(): passed invalid pointer %p supposed to be in bin for " 1550 "element size %ld\n", address, bin->element_size); 1551 return B_ERROR; 1552 } 1553 1554 // add the address to the page free list 1555 *(addr_t *)address = (addr_t)page->free_list; 1556 page->free_list = (addr_t *)address; 1557 page->free_count++; 1558 1559 if (page->free_count == bin->max_free_count) { 1560 // we are now empty, remove the page from the bin list 1561 MutexLocker pageLocker(heap->page_lock); 1562 heap_unlink_page(page, &bin->page_list); 1563 page->in_use = 0; 1564 heap_link_page(page, &area->free_pages); 1565 heap_free_pages_added(heap, area, 1); 1566 } else if (page->free_count == 1) { 1567 // we need to add ourselfs to the page list of the bin 1568 heap_link_page(page, &bin->page_list); 1569 } else { 1570 // we might need to move back in the free pages list 1571 if (page->next && page->next->free_count < page->free_count) { 1572 // move ourselfs so the list stays ordered 1573 heap_page *insert = page->next; 1574 while (insert->next 1575 && insert->next->free_count < page->free_count) 1576 insert = insert->next; 1577 1578 heap_unlink_page(page, &bin->page_list); 1579 1580 page->prev = insert; 1581 page->next = insert->next; 1582 if (page->next) 1583 page->next->prev = page; 1584 insert->next = page; 1585 } 1586 } 1587 } else { 1588 // large allocation, just return the pages to the page free list 1589 uint32 allocationID = page->allocation_id; 1590 uint32 maxPages = area->page_count - page->index; 1591 uint32 pageCount = 0; 1592 1593 MutexLocker pageLocker(heap->page_lock); 1594 for (uint32 i = 0; i < maxPages; i++) { 1595 // loop until we find the end of this allocation 1596 if (!page[i].in_use || page[i].bin_index != heap->bin_count 1597 || page[i].allocation_id != allocationID) 1598 break; 1599 1600 // this page still belongs to the same allocation 1601 page[i].in_use = 0; 1602 page[i].allocation_id = 0; 1603 1604 // return it to the free list 1605 heap_link_page(&page[i], &area->free_pages); 1606 pageCount++; 1607 } 1608 1609 heap_free_pages_added(heap, area, pageCount); 1610 } 1611 1612 T(Free((addr_t)address)); 1613 areaReadLocker.Unlock(); 1614 1615 if (heap->empty_areas > 1) { 1616 WriteLocker areaWriteLocker(heap->area_lock); 1617 MutexLocker pageLocker(heap->page_lock); 1618 1619 area = heap->areas; 1620 while (area != NULL && heap->empty_areas > 1) { 1621 heap_area *next = area->next; 1622 if (area->area >= 0 1623 && area->free_page_count == area->page_count 1624 && heap_remove_area(heap, area) == B_OK) { 1625 delete_area(area->area); 1626 heap->empty_areas--; 1627 } 1628 1629 area = next; 1630 } 1631 } 1632 1633 return B_OK; 1634 } 1635 1636 1637 #if KERNEL_HEAP_LEAK_CHECK 1638 extern "C" void 1639 heap_set_get_caller(heap_allocator* heap, addr_t (*getCaller)()) 1640 { 1641 heap->get_caller = getCaller; 1642 } 1643 #endif 1644 1645 1646 static status_t 1647 heap_realloc(heap_allocator *heap, void *address, void **newAddress, 1648 size_t newSize) 1649 { 1650 ReadLocker areaReadLocker(heap->area_lock); 1651 heap_area *area = heap->all_areas; 1652 while (area) { 1653 // since the all_areas list is ordered by base with the biggest 1654 // base at the top, we need only find the first area with a base 1655 // smaller than our address to become our only candidate for 1656 // reallocating 1657 if (area->base <= (addr_t)address) { 1658 if ((addr_t)address >= area->base + area->size) { 1659 // none of the other areas can contain the address as the list 1660 // is ordered 1661 return B_ENTRY_NOT_FOUND; 1662 } 1663 1664 // this area contains the allocation, we're done searching 1665 break; 1666 } 1667 1668 area = area->all_next; 1669 } 1670 1671 if (area == NULL) { 1672 // this address does not belong to us 1673 return B_ENTRY_NOT_FOUND; 1674 } 1675 1676 TRACE(("realloc(address = %p, newSize = %lu)\n", address, newSize)); 1677 1678 heap_page *page = &area->page_table[((addr_t)address - area->base) 1679 / heap->page_size]; 1680 if (page->bin_index > heap->bin_count) { 1681 panic("realloc(): page %p: invalid bin_index %d\n", page, 1682 page->bin_index); 1683 return B_ERROR; 1684 } 1685 1686 // find out the size of the old allocation first 1687 size_t minSize = 0; 1688 size_t maxSize = 0; 1689 if (page->bin_index < heap->bin_count) { 1690 // this was a small allocation 1691 heap_bin *bin = &heap->bins[page->bin_index]; 1692 maxSize = bin->element_size; 1693 if (page->bin_index > 0) 1694 minSize = heap->bins[page->bin_index - 1].element_size + 1; 1695 } else { 1696 // this was a large allocation 1697 uint32 allocationID = page->allocation_id; 1698 uint32 maxPages = area->page_count - page->index; 1699 maxSize = heap->page_size; 1700 1701 MutexLocker pageLocker(heap->page_lock); 1702 for (uint32 i = 1; i < maxPages; i++) { 1703 if (!page[i].in_use || page[i].bin_index != heap->bin_count 1704 || page[i].allocation_id != allocationID) 1705 break; 1706 1707 minSize += heap->page_size; 1708 maxSize += heap->page_size; 1709 } 1710 } 1711 1712 areaReadLocker.Unlock(); 1713 1714 #if KERNEL_HEAP_LEAK_CHECK 1715 newSize += sizeof(heap_leak_check_info); 1716 #endif 1717 1718 // does the new allocation simply fit in the old allocation? 1719 if (newSize > minSize && newSize <= maxSize) { 1720 #if KERNEL_HEAP_LEAK_CHECK 1721 // update the size info (the info is at the end so stays where it is) 1722 heap_leak_check_info *info = (heap_leak_check_info *)((addr_t)address 1723 + maxSize - sizeof(heap_leak_check_info)); 1724 info->size = newSize - sizeof(heap_leak_check_info); 1725 newSize -= sizeof(heap_leak_check_info); 1726 #endif 1727 1728 T(Reallocate((addr_t)address, (addr_t)address, newSize)); 1729 *newAddress = address; 1730 return B_OK; 1731 } 1732 1733 #if KERNEL_HEAP_LEAK_CHECK 1734 // new leak check info will be created with the malloc below 1735 newSize -= sizeof(heap_leak_check_info); 1736 #endif 1737 1738 // if not, allocate a new chunk of memory 1739 *newAddress = memalign(0, newSize); 1740 T(Reallocate((addr_t)address, (addr_t)*newAddress, newSize)); 1741 if (*newAddress == NULL) { 1742 // we tried but it didn't work out, but still the operation is done 1743 return B_OK; 1744 } 1745 1746 // copy the old data and free the old allocation 1747 memcpy(*newAddress, address, min_c(maxSize, newSize)); 1748 heap_free(heap, address); 1749 return B_OK; 1750 } 1751 1752 1753 inline uint32 1754 heap_class_for(size_t size) 1755 { 1756 #if KERNEL_HEAP_LEAK_CHECK 1757 // take the extra info size into account 1758 size += sizeof(heap_leak_check_info_s); 1759 #endif 1760 1761 uint32 index = 0; 1762 for (; index < HEAP_CLASS_COUNT - 1; index++) { 1763 if (size <= sHeapClasses[index].max_allocation_size) 1764 return index; 1765 } 1766 1767 return index; 1768 } 1769 1770 1771 static void 1772 deferred_deleter(void *arg, int iteration) 1773 { 1774 // move entries and deletables to on-stack lists 1775 InterruptsSpinLocker locker(sDeferredFreeListLock); 1776 if (sDeferredFreeList.IsEmpty() && sDeferredDeletableList.IsEmpty()) 1777 return; 1778 1779 DeferredFreeList entries; 1780 entries.MoveFrom(&sDeferredFreeList); 1781 1782 DeferredDeletableList deletables; 1783 deletables.MoveFrom(&sDeferredDeletableList); 1784 1785 locker.Unlock(); 1786 1787 // free the entries 1788 while (DeferredFreeListEntry* entry = entries.RemoveHead()) 1789 free(entry); 1790 1791 // delete the deletables 1792 while (DeferredDeletable* deletable = deletables.RemoveHead()) 1793 delete deletable; 1794 } 1795 1796 1797 // #pragma mark - 1798 1799 1800 static status_t 1801 heap_create_new_heap_area(heap_allocator *heap, const char *name, size_t size) 1802 { 1803 void *address = NULL; 1804 area_id heapArea = create_area(name, &address, 1805 B_ANY_KERNEL_BLOCK_ADDRESS, size, B_FULL_LOCK, 1806 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 1807 if (heapArea < B_OK) { 1808 TRACE(("heap: couldn't allocate heap area \"%s\"\n", name)); 1809 return heapArea; 1810 } 1811 1812 heap_add_area(heap, heapArea, (addr_t)address, size); 1813 #if PARANOID_HEAP_VALIDATION 1814 heap_validate_heap(heap); 1815 #endif 1816 return B_OK; 1817 } 1818 1819 1820 static int32 1821 heap_grow_thread(void *) 1822 { 1823 while (true) { 1824 // wait for a request to grow the heap list 1825 if (acquire_sem(sHeapGrowSem) < B_OK) 1826 continue; 1827 1828 if (sAddGrowHeap) { 1829 // the grow heap is going to run full soon, try to allocate a new 1830 // one to make some room. 1831 TRACE(("heap_grower: grow heaps will run out of memory soon\n")); 1832 if (heap_create_new_heap_area(sGrowHeap, "additional grow heap", 1833 HEAP_DEDICATED_GROW_SIZE) != B_OK) 1834 dprintf("heap_grower: failed to create new grow heap area\n"); 1835 } 1836 1837 for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) { 1838 heap_allocator *heap = sHeaps[i]; 1839 if (sLastGrowRequest[i] > sLastHandledGrowRequest[i] 1840 || heap_should_grow(heap)) { 1841 // grow this heap if it is nearly full or if a grow was 1842 // explicitly requested for this heap (happens when a large 1843 // allocation cannot be fulfilled due to lack of contiguous 1844 // pages) 1845 if (heap_create_new_heap_area(heap, "additional heap", 1846 HEAP_GROW_SIZE) != B_OK) 1847 dprintf("heap_grower: failed to create new heap area\n"); 1848 sLastHandledGrowRequest[i] = sLastGrowRequest[i]; 1849 } 1850 } 1851 1852 // notify anyone waiting for this request 1853 release_sem_etc(sHeapGrownNotify, -1, B_RELEASE_ALL); 1854 } 1855 1856 return 0; 1857 } 1858 1859 1860 status_t 1861 heap_init(addr_t base, size_t size) 1862 { 1863 for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) { 1864 size_t partSize = size * sHeapClasses[i].initial_percentage / 100; 1865 sHeaps[i] = heap_create_allocator(sHeapClasses[i].name, base, partSize, 1866 &sHeapClasses[i]); 1867 sLastGrowRequest[i] = sLastHandledGrowRequest[i] = 0; 1868 base += partSize; 1869 } 1870 1871 // set up some debug commands 1872 add_debugger_command_etc("heap", &dump_heap_list, 1873 "Dump infos about the kernel heap(s)", 1874 "[(\"grow\" | \"stats\" | <heap>)]\n" 1875 "Dump infos about the kernel heap(s). If \"grow\" is specified, only\n" 1876 "infos about the dedicated grow heap are printed. If \"stats\" is\n" 1877 "given as the argument, currently only the heap count is printed.\n" 1878 "If <heap> is given, it is interpreted as the address of the heap to\n" 1879 "print infos about.\n", 0); 1880 #if !KERNEL_HEAP_LEAK_CHECK 1881 add_debugger_command_etc("allocations", &dump_allocations, 1882 "Dump current heap allocations", 1883 "[\"stats\"] [<heap>]\n" 1884 "If no parameters are given, all current alloactions are dumped.\n" 1885 "If the optional argument \"stats\" is specified, only the allocation\n" 1886 "counts and no individual allocations are printed\n" 1887 "If a specific heap address is given, only allocations of this\n" 1888 "allocator are dumped\n", 0); 1889 #else // !KERNEL_HEAP_LEAK_CHECK 1890 add_debugger_command_etc("allocations", &dump_allocations, 1891 "Dump current heap allocations", 1892 "[(\"team\" | \"thread\") <id>] [ \"caller\" <address> ] [\"stats\"]\n" 1893 "If no parameters are given, all current alloactions are dumped.\n" 1894 "If \"team\", \"thread\", and/or \"caller\" is specified as the first\n" 1895 "argument, only allocations matching the team id, thread id, or \n" 1896 "caller address given in the second argument are printed.\n" 1897 "If the optional argument \"stats\" is specified, only the allocation\n" 1898 "counts and no individual allocations are printed\n", 0); 1899 add_debugger_command_etc("allocations_per_caller", 1900 &dump_allocations_per_caller, 1901 "Dump current heap allocations summed up per caller", 1902 "[ \"-c\" ] [ -h <heap> ]\n" 1903 "The current allocations will by summed up by caller (their count and\n" 1904 "size) printed in decreasing order by size or, if \"-c\" is\n" 1905 "specified, by allocation count. If given <heap> specifies the\n" 1906 "address of the heap for which to print the allocations.\n", 0); 1907 #endif // KERNEL_HEAP_LEAK_CHECK 1908 return B_OK; 1909 } 1910 1911 1912 status_t 1913 heap_init_post_sem() 1914 { 1915 #if PARANOID_KERNEL_MALLOC 1916 vm_block_address_range("uninitialized heap memory", 1917 (void *)ROUNDDOWN(0xcccccccc, B_PAGE_SIZE), B_PAGE_SIZE * 64); 1918 #endif 1919 #if PARANOID_KERNEL_FREE 1920 vm_block_address_range("freed heap memory", 1921 (void *)ROUNDDOWN(0xdeadbeef, B_PAGE_SIZE), B_PAGE_SIZE * 64); 1922 #endif 1923 1924 sHeapGrowSem = create_sem(0, "heap_grow_sem"); 1925 if (sHeapGrowSem < 0) { 1926 panic("heap_init_post_sem(): failed to create heap grow sem\n"); 1927 return B_ERROR; 1928 } 1929 1930 sHeapGrownNotify = create_sem(0, "heap_grown_notify"); 1931 if (sHeapGrownNotify < 0) { 1932 panic("heap_init_post_sem(): failed to create heap grown notify sem\n"); 1933 return B_ERROR; 1934 } 1935 1936 return B_OK; 1937 } 1938 1939 1940 status_t 1941 heap_init_post_thread() 1942 { 1943 void *address = NULL; 1944 area_id growHeapArea = create_area("dedicated grow heap", &address, 1945 B_ANY_KERNEL_BLOCK_ADDRESS, HEAP_DEDICATED_GROW_SIZE, B_FULL_LOCK, 1946 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 1947 if (growHeapArea < B_OK) { 1948 panic("heap_init_post_thread(): couldn't allocate dedicate grow heap " 1949 "area"); 1950 return growHeapArea; 1951 } 1952 1953 sGrowHeap = heap_create_allocator("grow", (addr_t)address, 1954 HEAP_DEDICATED_GROW_SIZE, &sHeapClasses[0]); 1955 if (sGrowHeap == NULL) { 1956 panic("heap_init_post_thread(): failed to create dedicated grow heap\n"); 1957 return B_ERROR; 1958 } 1959 1960 sHeapGrowThread = spawn_kernel_thread(heap_grow_thread, "heap grower", 1961 B_URGENT_PRIORITY, NULL); 1962 if (sHeapGrowThread < 0) { 1963 panic("heap_init_post_thread(): cannot create heap grow thread\n"); 1964 return sHeapGrowThread; 1965 } 1966 1967 // run the deferred deleter roughly once a second 1968 if (register_kernel_daemon(deferred_deleter, NULL, 10) != B_OK) 1969 panic("heap_init_post_thread(): failed to init deferred deleter"); 1970 1971 send_signal_etc(sHeapGrowThread, SIGCONT, B_DO_NOT_RESCHEDULE); 1972 return B_OK; 1973 } 1974 1975 1976 // #pragma mark - Public API 1977 1978 1979 void * 1980 memalign(size_t alignment, size_t size) 1981 { 1982 if (!gKernelStartup && !are_interrupts_enabled()) { 1983 panic("memalign(): called with interrupts disabled\n"); 1984 return NULL; 1985 } 1986 1987 if (!gKernelStartup && size > HEAP_AREA_USE_THRESHOLD) { 1988 // don't even attempt such a huge allocation - use areas instead 1989 size_t areaSize = size + sizeof(area_allocation_info); 1990 if (alignment != 0) 1991 areaSize = ROUNDUP(areaSize, alignment); 1992 1993 void *address = NULL; 1994 areaSize = ROUNDUP(areaSize, B_PAGE_SIZE); 1995 area_id allocationArea = create_area("memalign area", &address, 1996 B_ANY_KERNEL_BLOCK_ADDRESS, areaSize, B_FULL_LOCK, 1997 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 1998 if (allocationArea < B_OK) { 1999 dprintf("heap: failed to create area for huge allocation\n"); 2000 return NULL; 2001 } 2002 2003 area_allocation_info *info = (area_allocation_info *)address; 2004 info->magic = kAreaAllocationMagic; 2005 info->area = allocationArea; 2006 info->base = address; 2007 info->size = areaSize; 2008 info->allocation_size = size; 2009 info->allocation_alignment = alignment; 2010 2011 address = (void *)((addr_t)address + sizeof(area_allocation_info)); 2012 if (alignment != 0) 2013 address = (void *)ROUNDUP((addr_t)address, alignment); 2014 2015 TRACE(("heap: allocated area %ld for huge allocation of %lu bytes\n", 2016 allocationArea, size)); 2017 2018 info->allocation_base = address; 2019 2020 #if PARANOID_KERNEL_MALLOC 2021 memset(address, 0xcc, size); 2022 #endif 2023 return address; 2024 } 2025 2026 uint32 heapClass = heap_class_for(size); 2027 heap_allocator *heap = sHeaps[heapClass]; 2028 void *result = heap_memalign(heap, alignment, size); 2029 if (result == NULL) { 2030 // request an urgent grow and wait - we don't do it ourselfs here to 2031 // serialize growing through the grow thread, as otherwise multiple 2032 // threads hitting this situation (likely when memory ran out) would 2033 // all add areas 2034 sLastGrowRequest[heapClass]++; 2035 switch_sem(sHeapGrowSem, sHeapGrownNotify); 2036 2037 // and then try again 2038 result = heap_memalign(heap, alignment, size); 2039 } else if (heap_should_grow(heap)) { 2040 // should grow sometime soon, notify the grower 2041 release_sem_etc(sHeapGrowSem, 1, B_DO_NOT_RESCHEDULE); 2042 } 2043 2044 #if PARANOID_HEAP_VALIDATION 2045 heap_validate_heap(heap); 2046 #endif 2047 2048 if (result == NULL) 2049 panic("heap: kernel heap has run out of memory\n"); 2050 return result; 2051 } 2052 2053 2054 void * 2055 memalign_nogrow(size_t alignment, size_t size) 2056 { 2057 // use dedicated memory in the grow thread by default 2058 if (thread_get_current_thread_id() == sHeapGrowThread) { 2059 void *result = heap_memalign(sGrowHeap, alignment, size); 2060 if (!sAddGrowHeap && heap_should_grow(sGrowHeap)) { 2061 // hopefully the heap grower will manage to create a new heap 2062 // before running out of private memory... 2063 dprintf("heap: requesting new grow heap\n"); 2064 sAddGrowHeap = true; 2065 release_sem_etc(sHeapGrowSem, 1, B_DO_NOT_RESCHEDULE); 2066 } 2067 2068 if (result != NULL) 2069 return result; 2070 } 2071 2072 // try public memory, there might be something available 2073 heap_allocator *heap = sHeaps[heap_class_for(size)]; 2074 void *result = heap_memalign(heap, alignment, size); 2075 if (result != NULL) 2076 return result; 2077 2078 // no memory available 2079 if (thread_get_current_thread_id() == sHeapGrowThread) 2080 panic("heap: all heaps have run out of memory while growing\n"); 2081 else 2082 dprintf("heap: all heaps have run out of memory\n"); 2083 2084 return NULL; 2085 } 2086 2087 2088 void * 2089 malloc_nogrow(size_t size) 2090 { 2091 return memalign_nogrow(0, size); 2092 } 2093 2094 2095 void * 2096 malloc(size_t size) 2097 { 2098 return memalign(0, size); 2099 } 2100 2101 2102 void 2103 free(void *address) 2104 { 2105 if (!gKernelStartup && !are_interrupts_enabled()) { 2106 panic("free(): called with interrupts disabled\n"); 2107 return; 2108 } 2109 2110 for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) { 2111 heap_allocator *heap = sHeaps[i]; 2112 if (heap_free(heap, address) == B_OK) { 2113 #if PARANOID_HEAP_VALIDATION 2114 heap_validate_heap(heap); 2115 #endif 2116 return; 2117 } 2118 } 2119 2120 // maybe it was allocated from the dedicated grow heap 2121 if (heap_free(sGrowHeap, address) == B_OK) 2122 return; 2123 2124 // or maybe it was a huge allocation using an area 2125 area_info areaInfo; 2126 area_id area = area_for(address); 2127 if (area >= B_OK && get_area_info(area, &areaInfo) == B_OK) { 2128 area_allocation_info *info = (area_allocation_info *)areaInfo.address; 2129 2130 // just make extra sure it was allocated by us 2131 if (info->magic == kAreaAllocationMagic && info->area == area 2132 && info->size == areaInfo.size && info->base == areaInfo.address 2133 && info->allocation_size < areaInfo.size) { 2134 delete_area(area); 2135 TRACE(("free(): freed huge allocation by deleting area %ld\n", 2136 area)); 2137 return; 2138 } 2139 } 2140 2141 panic("free(): free failed for address %p\n", address); 2142 } 2143 2144 2145 void * 2146 realloc(void *address, size_t newSize) 2147 { 2148 if (!gKernelStartup && !are_interrupts_enabled()) { 2149 panic("realloc(): called with interrupts disabled\n"); 2150 return NULL; 2151 } 2152 2153 if (address == NULL) 2154 return memalign(0, newSize); 2155 2156 if (newSize == 0) { 2157 free(address); 2158 return NULL; 2159 } 2160 2161 void *newAddress = NULL; 2162 for (uint32 i = 0; i < HEAP_CLASS_COUNT; i++) { 2163 heap_allocator *heap = sHeaps[i]; 2164 if (heap_realloc(heap, address, &newAddress, newSize) == B_OK) { 2165 #if PARANOID_HEAP_VALIDATION 2166 heap_validate_heap(heap); 2167 #endif 2168 return newAddress; 2169 } 2170 } 2171 2172 // maybe it was allocated from the dedicated grow heap 2173 if (heap_realloc(sGrowHeap, address, &newAddress, newSize) == B_OK) 2174 return newAddress; 2175 2176 // or maybe it was a huge allocation using an area 2177 area_info areaInfo; 2178 area_id area = area_for(address); 2179 if (area >= B_OK && get_area_info(area, &areaInfo) == B_OK) { 2180 area_allocation_info *info = (area_allocation_info *)areaInfo.address; 2181 2182 // just make extra sure it was allocated by us 2183 if (info->magic == kAreaAllocationMagic && info->area == area 2184 && info->size == areaInfo.size && info->base == areaInfo.address 2185 && info->allocation_size < areaInfo.size) { 2186 size_t available = info->size - ((addr_t)info->allocation_base 2187 - (addr_t)info->base); 2188 2189 if (available >= newSize) { 2190 // there is enough room available for the newSize 2191 TRACE(("realloc(): new size %ld fits in old area %ld with %ld " 2192 "available\n", newSize, area, available)); 2193 return address; 2194 } 2195 2196 // have to allocate/copy/free - TODO maybe resize the area instead? 2197 newAddress = memalign(0, newSize); 2198 if (newAddress == NULL) { 2199 dprintf("realloc(): failed to allocate new block of %ld bytes\n", 2200 newSize); 2201 return NULL; 2202 } 2203 2204 memcpy(newAddress, address, min_c(newSize, info->allocation_size)); 2205 delete_area(area); 2206 TRACE(("realloc(): allocated new block %p for size %ld and deleted " 2207 "old area %ld\n", newAddress, newSize, area)); 2208 return newAddress; 2209 } 2210 } 2211 2212 panic("realloc(): failed to realloc address %p to size %lu\n", address, 2213 newSize); 2214 return NULL; 2215 } 2216 2217 2218 void * 2219 calloc(size_t numElements, size_t size) 2220 { 2221 void *address = memalign(0, numElements * size); 2222 if (address != NULL) 2223 memset(address, 0, numElements * size); 2224 2225 return address; 2226 } 2227 2228 2229 void 2230 deferred_free(void *block) 2231 { 2232 if (block == NULL) 2233 return; 2234 2235 DeferredFreeListEntry *entry = new(block) DeferredFreeListEntry; 2236 2237 InterruptsSpinLocker _(sDeferredFreeListLock); 2238 sDeferredFreeList.Add(entry); 2239 } 2240 2241 2242 void * 2243 malloc_referenced(size_t size) 2244 { 2245 int32 *referencedData = (int32 *)malloc(size + 4); 2246 if (referencedData == NULL) 2247 return NULL; 2248 2249 *referencedData = 1; 2250 return referencedData + 1; 2251 } 2252 2253 2254 void * 2255 malloc_referenced_acquire(void *data) 2256 { 2257 if (data != NULL) { 2258 int32 *referencedData = (int32 *)data - 1; 2259 atomic_add(referencedData, 1); 2260 } 2261 2262 return data; 2263 } 2264 2265 2266 void 2267 malloc_referenced_release(void *data) 2268 { 2269 if (data == NULL) 2270 return; 2271 2272 int32 *referencedData = (int32 *)data - 1; 2273 if (atomic_add(referencedData, -1) < 1) 2274 free(referencedData); 2275 } 2276 2277 2278 DeferredDeletable::~DeferredDeletable() 2279 { 2280 } 2281 2282 2283 void 2284 deferred_delete(DeferredDeletable *deletable) 2285 { 2286 if (deletable == NULL) 2287 return; 2288 2289 InterruptsSpinLocker _(sDeferredFreeListLock); 2290 sDeferredDeletableList.Add(deletable); 2291 } 2292