00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00036 #include <malloc.h>
00037 #include <bool.h>
00038 #include <as.h>
00039 #include <align.h>
00040 #include <macros.h>
00041 #include <assert.h>
00042 #include <errno.h>
00043 #include <bitops.h>
00044 #include <mem.h>
00045 #include <futex.h>
00046 #include <stdlib.h>
00047 #include <adt/gcdlcm.h>
00048 #include "private/malloc.h"
00049
00051 #define HEAP_BLOCK_HEAD_MAGIC UINT32_C(0xBEEF0101)
00052
00054 #define HEAP_BLOCK_FOOT_MAGIC UINT32_C(0xBEEF0202)
00055
00057 #define HEAP_AREA_MAGIC UINT32_C(0xBEEFCAFE)
00058
00065 #define BASE_ALIGN 16
00066
00075 #define SHRINK_GRANULARITY (64 * PAGE_SIZE)
00076
00078 #define STRUCT_OVERHEAD \
00079 (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
00080
00082 #define AREA_OVERHEAD(size) \
00083 (ALIGN_UP(size + sizeof(heap_area_t), BASE_ALIGN))
00084
00090 #define GROSS_SIZE(size) ((size) + STRUCT_OVERHEAD)
00091
00097 #define NET_SIZE(size) ((size) - STRUCT_OVERHEAD)
00098
00102 #define AREA_FIRST_BLOCK_HEAD(area) \
00103 (ALIGN_UP(((uintptr_t) (area)) + sizeof(heap_area_t), BASE_ALIGN))
00104
00108 #define AREA_LAST_BLOCK_FOOT(area) \
00109 (((uintptr_t) (area)->end) - sizeof(heap_block_foot_t))
00110
00114 #define BLOCK_HEAD(foot) \
00115 ((heap_block_head_t *) \
00116 (((uintptr_t) (foot)) + sizeof(heap_block_foot_t) - (foot)->size))
00117
00121 #define BLOCK_FOOT(head) \
00122 ((heap_block_foot_t *) \
00123 (((uintptr_t) (head)) + (head)->size - sizeof(heap_block_foot_t)))
00124
00133 typedef struct heap_area {
00139 void *start;
00140
00142 void *end;
00143
00145 struct heap_area *prev;
00146
00148 struct heap_area *next;
00149
00151 uint32_t magic;
00152 } heap_area_t;
00153
00157 typedef struct {
00158
00159 size_t size;
00160
00161
00162 bool free;
00163
00165 heap_area_t *area;
00166
00167
00168 uint32_t magic;
00169 } heap_block_head_t;
00170
00174 typedef struct {
00175
00176 size_t size;
00177
00178
00179 uint32_t magic;
00180 } heap_block_foot_t;
00181
00183 static heap_area_t *first_heap_area = NULL;
00184
00186 static heap_area_t *last_heap_area = NULL;
00187
00189 static heap_block_head_t *next_fit = NULL;
00190
00192 static futex_t malloc_futex = FUTEX_INITIALIZER;
00193
00194 #ifndef NDEBUG
00195
00196 #define malloc_assert(expr) \
00197 do { \
00198 if (!(expr)) {\
00199 futex_up(&malloc_futex); \
00200 assert_abort(#expr, __FILE__, __LINE__); \
00201 } \
00202 } while (0)
00203
00204 #else
00205
00206 #define malloc_assert(expr)
00207
00208 #endif
00209
00221 static void block_init(void *addr, size_t size, bool free, heap_area_t *area)
00222 {
00223
00224 heap_block_head_t *head = (heap_block_head_t *) addr;
00225
00226 head->size = size;
00227 head->free = free;
00228 head->area = area;
00229 head->magic = HEAP_BLOCK_HEAD_MAGIC;
00230
00231 heap_block_foot_t *foot = BLOCK_FOOT(head);
00232
00233 foot->size = size;
00234 foot->magic = HEAP_BLOCK_FOOT_MAGIC;
00235 }
00236
00246 static void block_check(void *addr)
00247 {
00248 heap_block_head_t *head = (heap_block_head_t *) addr;
00249
00250 malloc_assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
00251
00252 heap_block_foot_t *foot = BLOCK_FOOT(head);
00253
00254 malloc_assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
00255 malloc_assert(head->size == foot->size);
00256 }
00257
00265 static void area_check(void *addr)
00266 {
00267 heap_area_t *area = (heap_area_t *) addr;
00268
00269 malloc_assert(area->magic == HEAP_AREA_MAGIC);
00270 malloc_assert(addr == area->start);
00271 malloc_assert(area->start < area->end);
00272 malloc_assert(((uintptr_t) area->start % PAGE_SIZE) == 0);
00273 malloc_assert(((uintptr_t) area->end % PAGE_SIZE) == 0);
00274 }
00275
00283 static bool area_create(size_t size)
00284 {
00285 void *start = as_get_mappable_page(size);
00286 if (start == NULL)
00287 return false;
00288
00289
00290 void *astart = (void *) ALIGN_UP((uintptr_t) start, PAGE_SIZE);
00291 size_t asize = ALIGN_UP(size, PAGE_SIZE);
00292
00293 astart = as_area_create(astart, asize, AS_AREA_WRITE | AS_AREA_READ | AS_AREA_CACHEABLE);
00294 if (astart == (void *) -1)
00295 return false;
00296
00297 heap_area_t *area = (heap_area_t *) astart;
00298
00299 area->start = astart;
00300 area->end = (void *) ((uintptr_t) astart + asize);
00301 area->prev = NULL;
00302 area->next = NULL;
00303 area->magic = HEAP_AREA_MAGIC;
00304
00305 void *block = (void *) AREA_FIRST_BLOCK_HEAD(area);
00306 size_t bsize = (size_t) (area->end - block);
00307
00308 block_init(block, bsize, true, area);
00309
00310 if (last_heap_area == NULL) {
00311 first_heap_area = area;
00312 last_heap_area = area;
00313 } else {
00314 area->prev = last_heap_area;
00315 last_heap_area->next = area;
00316 last_heap_area = area;
00317 }
00318
00319 return true;
00320 }
00321
00332 static bool area_grow(heap_area_t *area, size_t size)
00333 {
00334 if (size == 0)
00335 return true;
00336
00337 area_check(area);
00338
00339
00340 size_t gross_size = (size_t) (area->end - area->start) + size;
00341 size_t asize = ALIGN_UP(gross_size, PAGE_SIZE);
00342 void *end = (void *) ((uintptr_t) area->start + asize);
00343
00344
00345 if (end < area->start)
00346 return false;
00347
00348
00349 int ret = as_area_resize(area->start, asize, 0);
00350 if (ret != EOK)
00351 return false;
00352
00353
00354 size_t net_size = (size_t) (end - area->end);
00355 if (net_size > 0)
00356 block_init(area->end, net_size, true, area);
00357
00358
00359 area->end = end;
00360
00361 return true;
00362 }
00363
00371 static bool heap_grow(size_t size)
00372 {
00373 if (size == 0)
00374 return true;
00375
00376
00377 for (heap_area_t *area = first_heap_area; area != NULL;
00378 area = area->next) {
00379 if (area_grow(area, size))
00380 return true;
00381 }
00382
00383
00384 return area_create(AREA_OVERHEAD(size));
00385 }
00386
00395 static void heap_shrink(heap_area_t *area)
00396 {
00397 area_check(area);
00398
00399 heap_block_foot_t *last_foot =
00400 (heap_block_foot_t *) AREA_LAST_BLOCK_FOOT(area);
00401 heap_block_head_t *last_head = BLOCK_HEAD(last_foot);
00402
00403 block_check((void *) last_head);
00404 malloc_assert(last_head->area == area);
00405
00406 if (last_head->free) {
00407
00408
00409
00410
00411
00412
00413 heap_block_head_t *first_head =
00414 (heap_block_head_t *) AREA_FIRST_BLOCK_HEAD(area);
00415
00416 block_check((void *) first_head);
00417 malloc_assert(first_head->area == area);
00418
00419 size_t shrink_size = ALIGN_DOWN(last_head->size, PAGE_SIZE);
00420
00421 if (first_head == last_head) {
00422
00423
00424
00425
00426
00427
00428 heap_area_t *prev = area->prev;
00429 heap_area_t *next = area->next;
00430
00431 if (prev != NULL) {
00432 area_check(prev);
00433 prev->next = next;
00434 } else
00435 first_heap_area = next;
00436
00437 if (next != NULL) {
00438 area_check(next);
00439 next->prev = prev;
00440 } else
00441 last_heap_area = prev;
00442
00443 as_area_destroy(area->start);
00444 } else if (shrink_size >= SHRINK_GRANULARITY) {
00445
00446
00447
00448
00449
00450
00451 size_t asize = (size_t) (area->end - area->start) - shrink_size;
00452 void *end = (void *) ((uintptr_t) area->start + asize);
00453
00454
00455 int ret = as_area_resize(area->start, asize, 0);
00456 if (ret != EOK)
00457 abort();
00458
00459
00460 area->end = end;
00461 size_t excess = ((size_t) area->end) - ((size_t) last_head);
00462
00463 if (excess > 0) {
00464 if (excess >= STRUCT_OVERHEAD) {
00465
00466
00467
00468
00469
00470 block_init((void *) last_head, excess, true, area);
00471 } else {
00472
00473
00474
00475
00476 heap_block_foot_t *prev_foot = (heap_block_foot_t *)
00477 (((uintptr_t) last_head) - sizeof(heap_block_foot_t));
00478 heap_block_head_t *prev_head = BLOCK_HEAD(prev_foot);
00479
00480 block_check((void *) prev_head);
00481
00482 block_init(prev_head, prev_head->size + excess,
00483 prev_head->free, area);
00484 }
00485 }
00486 }
00487 }
00488
00489 next_fit = NULL;
00490 }
00491
00499 void __malloc_init(void)
00500 {
00501 if (!area_create(PAGE_SIZE))
00502 abort();
00503 }
00504
00514 static void split_mark(heap_block_head_t *cur, const size_t size)
00515 {
00516 malloc_assert(cur->size >= size);
00517
00518
00519 size_t split_limit = GROSS_SIZE(size);
00520
00521 if (cur->size > split_limit) {
00522
00523 void *next = ((void *) cur) + size;
00524 block_init(next, cur->size - size, true, cur->area);
00525 block_init(cur, size, false, cur->area);
00526 } else {
00527
00528 cur->free = false;
00529 }
00530 }
00531
00548 static void *malloc_area(heap_area_t *area, heap_block_head_t *first_block,
00549 heap_block_head_t *final_block, size_t real_size, size_t falign)
00550 {
00551 area_check((void *) area);
00552 malloc_assert((void *) first_block >= (void *) AREA_FIRST_BLOCK_HEAD(area));
00553 malloc_assert((void *) first_block < area->end);
00554
00555 for (heap_block_head_t *cur = first_block; (void *) cur < area->end;
00556 cur = (heap_block_head_t *) (((void *) cur) + cur->size)) {
00557 block_check(cur);
00558
00559
00560 if ((final_block != NULL) && (cur == final_block))
00561 break;
00562
00563
00564 if ((cur->free) && (cur->size >= real_size)) {
00565
00566
00567
00568
00569 void *addr = (void *)
00570 ((uintptr_t) cur + sizeof(heap_block_head_t));
00571 void *aligned = (void *)
00572 ALIGN_UP((uintptr_t) addr, falign);
00573
00574 if (addr == aligned) {
00575
00576 split_mark(cur, real_size);
00577
00578 next_fit = cur;
00579 return addr;
00580 } else {
00581
00582 size_t excess = (size_t) (aligned - addr);
00583
00584 if (cur->size >= real_size + excess) {
00585
00586
00587
00588
00589 if ((void *) cur > (void *) AREA_FIRST_BLOCK_HEAD(area)) {
00590
00591
00592
00593
00594
00595 heap_block_foot_t *prev_foot = (heap_block_foot_t *)
00596 ((void *) cur - sizeof(heap_block_foot_t));
00597
00598 heap_block_head_t *prev_head = (heap_block_head_t *)
00599 ((void *) cur - prev_foot->size);
00600
00601 block_check(prev_head);
00602
00603 size_t reduced_size = cur->size - excess;
00604 heap_block_head_t *next_head = ((void *) cur) + excess;
00605
00606 if ((!prev_head->free) &&
00607 (excess >= STRUCT_OVERHEAD)) {
00608
00609
00610
00611
00612
00613
00614 block_init(cur, excess, true, area);
00615 } else {
00616
00617
00618
00619
00620
00621
00622
00623 block_init(prev_head, prev_head->size + excess,
00624 prev_head->free, area);
00625 }
00626
00627 block_init(next_head, reduced_size, true, area);
00628 split_mark(next_head, real_size);
00629
00630 next_fit = next_head;
00631 return aligned;
00632 } else {
00633
00634
00635
00636
00637
00638
00639
00640 while (excess < STRUCT_OVERHEAD) {
00641 aligned += falign;
00642 excess += falign;
00643 }
00644
00645
00646 if (cur->size >= real_size + excess) {
00647 size_t reduced_size = cur->size - excess;
00648 cur = (heap_block_head_t *)
00649 (AREA_FIRST_BLOCK_HEAD(area) + excess);
00650
00651 block_init((void *) AREA_FIRST_BLOCK_HEAD(area),
00652 excess, true, area);
00653 block_init(cur, reduced_size, true, area);
00654 split_mark(cur, real_size);
00655
00656 next_fit = cur;
00657 return aligned;
00658 }
00659 }
00660 }
00661 }
00662 }
00663 }
00664
00665 return NULL;
00666 }
00667
00678 static void *malloc_internal(const size_t size, const size_t align)
00679 {
00680 malloc_assert(first_heap_area != NULL);
00681
00682 if (align == 0)
00683 return NULL;
00684
00685 size_t falign = lcm(align, BASE_ALIGN);
00686 size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign));
00687
00688 bool retry = false;
00689 heap_block_head_t *split;
00690
00691 loop:
00692
00693
00694 split = next_fit;
00695
00696 if (split != NULL) {
00697 void *addr = malloc_area(split->area, split, NULL, real_size,
00698 falign);
00699
00700 if (addr != NULL)
00701 return addr;
00702 }
00703
00704
00705 for (heap_area_t *area = first_heap_area; area != NULL;
00706 area = area->next) {
00707 heap_block_head_t *first = (heap_block_head_t *)
00708 AREA_FIRST_BLOCK_HEAD(area);
00709
00710 void *addr = malloc_area(area, first, split, real_size,
00711 falign);
00712
00713 if (addr != NULL)
00714 return addr;
00715 }
00716
00717 if (!retry) {
00718
00719 if (heap_grow(real_size)) {
00720 retry = true;
00721 goto loop;
00722 }
00723 }
00724
00725 return NULL;
00726 }
00727
00736 void *calloc(const size_t nmemb, const size_t size)
00737 {
00738 void *block = malloc(nmemb * size);
00739 if (block == NULL)
00740 return NULL;
00741
00742 memset(block, 0, nmemb * size);
00743 return block;
00744 }
00745
00753 void *malloc(const size_t size)
00754 {
00755 futex_down(&malloc_futex);
00756 void *block = malloc_internal(size, BASE_ALIGN);
00757 futex_up(&malloc_futex);
00758
00759 return block;
00760 }
00761
00770 void *memalign(const size_t align, const size_t size)
00771 {
00772 if (align == 0)
00773 return NULL;
00774
00775 size_t palign =
00776 1 << (fnzb(max(sizeof(void *), align) - 1) + 1);
00777
00778 futex_down(&malloc_futex);
00779 void *block = malloc_internal(size, palign);
00780 futex_up(&malloc_futex);
00781
00782 return block;
00783 }
00784
00793 void *realloc(const void *addr, const size_t size)
00794 {
00795 if (addr == NULL)
00796 return malloc(size);
00797
00798 futex_down(&malloc_futex);
00799
00800
00801 heap_block_head_t *head =
00802 (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
00803
00804 block_check(head);
00805 malloc_assert(!head->free);
00806
00807 heap_area_t *area = head->area;
00808
00809 area_check(area);
00810 malloc_assert((void *) head >= (void *) AREA_FIRST_BLOCK_HEAD(area));
00811 malloc_assert((void *) head < area->end);
00812
00813 void *ptr = NULL;
00814 bool reloc = false;
00815 size_t real_size = GROSS_SIZE(ALIGN_UP(size, BASE_ALIGN));
00816 size_t orig_size = head->size;
00817
00818 if (orig_size > real_size) {
00819
00820 if (orig_size - real_size >= STRUCT_OVERHEAD) {
00821
00822
00823
00824
00825 block_init((void *) head, real_size, false, area);
00826 block_init((void *) head + real_size,
00827 orig_size - real_size, true, area);
00828 heap_shrink(area);
00829 }
00830
00831 ptr = ((void *) head) + sizeof(heap_block_head_t);
00832 } else {
00833
00834
00835
00836
00837
00838
00839 heap_block_head_t *next_head =
00840 (heap_block_head_t *) (((void *) head) + head->size);
00841
00842 if (((void *) next_head < area->end) &&
00843 (head->size + next_head->size >= real_size) &&
00844 (next_head->free)) {
00845 block_check(next_head);
00846 block_init(head, head->size + next_head->size, false, area);
00847 split_mark(head, real_size);
00848
00849 ptr = ((void *) head) + sizeof(heap_block_head_t);
00850 next_fit = NULL;
00851 } else
00852 reloc = true;
00853 }
00854
00855 futex_up(&malloc_futex);
00856
00857 if (reloc) {
00858 ptr = malloc(size);
00859 if (ptr != NULL) {
00860 memcpy(ptr, addr, NET_SIZE(orig_size));
00861 free(addr);
00862 }
00863 }
00864
00865 return ptr;
00866 }
00867
00873 void free(const void *addr)
00874 {
00875 futex_down(&malloc_futex);
00876
00877
00878 heap_block_head_t *head
00879 = (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
00880
00881 block_check(head);
00882 malloc_assert(!head->free);
00883
00884 heap_area_t *area = head->area;
00885
00886 area_check(area);
00887 malloc_assert((void *) head >= (void *) AREA_FIRST_BLOCK_HEAD(area));
00888 malloc_assert((void *) head < area->end);
00889
00890
00891 head->free = true;
00892
00893
00894 heap_block_head_t *next_head
00895 = (heap_block_head_t *) (((void *) head) + head->size);
00896
00897 if ((void *) next_head < area->end) {
00898 block_check(next_head);
00899 if (next_head->free)
00900 block_init(head, head->size + next_head->size, true, area);
00901 }
00902
00903
00904 if ((void *) head > (void *) AREA_FIRST_BLOCK_HEAD(area)) {
00905 heap_block_foot_t *prev_foot =
00906 (heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t));
00907
00908 heap_block_head_t *prev_head =
00909 (heap_block_head_t *) (((void *) head) - prev_foot->size);
00910
00911 block_check(prev_head);
00912
00913 if (prev_head->free)
00914 block_init(prev_head, prev_head->size + head->size, true,
00915 area);
00916 }
00917
00918 heap_shrink(area);
00919
00920 futex_up(&malloc_futex);
00921 }
00922
00923 void *heap_check(void)
00924 {
00925 futex_down(&malloc_futex);
00926
00927 if (first_heap_area == NULL) {
00928 futex_up(&malloc_futex);
00929 return (void *) -1;
00930 }
00931
00932
00933 for (heap_area_t *area = first_heap_area; area != NULL;
00934 area = area->next) {
00935
00936
00937 if ((area->magic != HEAP_AREA_MAGIC) ||
00938 ((void *) area != area->start) ||
00939 (area->start >= area->end) ||
00940 (((uintptr_t) area->start % PAGE_SIZE) != 0) ||
00941 (((uintptr_t) area->end % PAGE_SIZE) != 0)) {
00942 futex_up(&malloc_futex);
00943 return (void *) area;
00944 }
00945
00946
00947 for (heap_block_head_t *head = (heap_block_head_t *)
00948 AREA_FIRST_BLOCK_HEAD(area); (void *) head < area->end;
00949 head = (heap_block_head_t *) (((void *) head) + head->size)) {
00950
00951
00952 if (head->magic != HEAP_BLOCK_HEAD_MAGIC) {
00953 futex_up(&malloc_futex);
00954 return (void *) head;
00955 }
00956
00957 heap_block_foot_t *foot = BLOCK_FOOT(head);
00958
00959 if ((foot->magic != HEAP_BLOCK_FOOT_MAGIC) ||
00960 (head->size != foot->size)) {
00961 futex_up(&malloc_futex);
00962 return (void *) foot;
00963 }
00964 }
00965 }
00966
00967 futex_up(&malloc_futex);
00968
00969 return NULL;
00970 }
00971