Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/c/generic/malloc.c

    r47b7006 r207533f  
    4444#include <mem.h>
    4545#include <futex.h>
     46#include <stdlib.h>
    4647#include <adt/gcdlcm.h>
    4748#include "private/malloc.h"
    4849
    49 /* Magic used in heap headers. */
    50 #define HEAP_BLOCK_HEAD_MAGIC  0xBEEF0101
    51 
    52 /* Magic used in heap footers. */
    53 #define HEAP_BLOCK_FOOT_MAGIC  0xBEEF0202
    54 
    55 /** Allocation alignment (this also covers the alignment of fields
    56     in the heap header and footer) */
     50/** Magic used in heap headers. */
     51#define HEAP_BLOCK_HEAD_MAGIC  UINT32_C(0xBEEF0101)
     52
     53/** Magic used in heap footers. */
     54#define HEAP_BLOCK_FOOT_MAGIC  UINT32_C(0xBEEF0202)
     55
     56/** Magic used in heap descriptor. */
     57#define HEAP_AREA_MAGIC  UINT32_C(0xBEEFCAFE)
     58
     59/** Allocation alignment.
     60 *
     61 * This also covers the alignment of fields
     62 * in the heap header and footer.
     63 *
     64 */
    5765#define BASE_ALIGN  16
    5866
    59 /**
    60  * Either 4 * 256M on 32-bit architecures or 16 * 256M on 64-bit architectures
    61  */
    62 #define MAX_HEAP_SIZE  (sizeof(uintptr_t) << 28)
    63 
    64 /**
    65  *
    66  */
    67 #define STRUCT_OVERHEAD  (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
    68 
    69 /**
    70  * Calculate real size of a heap block (with header and footer)
     67/** Heap shrink granularity
     68 *
     69 * Try not to pump and stress the heap to much
     70 * by shrinking and enlarging it too often.
     71 * A heap area won't shrunk if it the released
     72 * free block is smaller than this constant.
     73 *
     74 */
     75#define SHRINK_GRANULARITY  (64 * PAGE_SIZE)
     76
     77/** Overhead of each heap block. */
     78#define STRUCT_OVERHEAD \
     79        (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
     80
     81/** Overhead of each area. */
     82#define AREA_OVERHEAD(size) \
     83        (ALIGN_UP(size + sizeof(heap_area_t), BASE_ALIGN))
     84
     85/** Calculate real size of a heap block.
     86 *
     87 * Add header and footer size.
     88 *
    7189 */
    7290#define GROSS_SIZE(size)  ((size) + STRUCT_OVERHEAD)
    7391
    74 /**
    75  * Calculate net size of a heap block (without header and footer)
     92/** Calculate net size of a heap block.
     93 *
     94 * Subtract header and footer size.
     95 *
    7696 */
    7797#define NET_SIZE(size)  ((size) - STRUCT_OVERHEAD)
     98
     99/** Get first block in heap area.
     100 *
     101 */
     102#define AREA_FIRST_BLOCK_HEAD(area) \
     103        (ALIGN_UP(((uintptr_t) (area)) + sizeof(heap_area_t), BASE_ALIGN))
     104
     105/** Get last block in heap area.
     106 *
     107 */
     108#define AREA_LAST_BLOCK_FOOT(area) \
     109        (((uintptr_t) (area)->end) - sizeof(heap_block_foot_t))
     110
     111/** Get header in heap block.
     112 *
     113 */
     114#define BLOCK_HEAD(foot) \
     115        ((heap_block_head_t *) \
     116            (((uintptr_t) (foot)) + sizeof(heap_block_foot_t) - (foot)->size))
     117
     118/** Get footer in heap block.
     119 *
     120 */
     121#define BLOCK_FOOT(head) \
     122        ((heap_block_foot_t *) \
     123            (((uintptr_t) (head)) + (head)->size - sizeof(heap_block_foot_t)))
     124
     125/** Heap area.
     126 *
     127 * The memory managed by the heap allocator is divided into
     128 * multiple discontinuous heaps. Each heap is represented
     129 * by a separate address space area which has this structure
     130 * at its very beginning.
     131 *
     132 */
     133typedef struct heap_area {
     134        /** Start of the heap area (including this structure)
     135         *
     136         * Aligned on page boundary.
     137         *
     138         */
     139        void *start;
     140       
     141        /** End of the heap area (aligned on page boundary) */
     142        void *end;
     143       
     144        /** Previous heap area */
     145        struct heap_area *prev;
     146       
     147        /** Next heap area */
     148        struct heap_area *next;
     149       
     150        /** A magic value */
     151        uint32_t magic;
     152} heap_area_t;
    78153
    79154/** Header of a heap block
     
    87162        bool free;
    88163       
     164        /** Heap area this block belongs to */
     165        heap_area_t *area;
     166       
    89167        /* A magic value to detect overwrite of heap header */
    90168        uint32_t magic;
     
    102180} heap_block_foot_t;
    103181
    104 /** Linker heap symbol */
    105 extern char _heap;
     182/** First heap area */
     183static heap_area_t *first_heap_area = NULL;
     184
     185/** Last heap area */
     186static heap_area_t *last_heap_area = NULL;
     187
     188/** Next heap block to examine (next fit algorithm) */
     189static heap_block_head_t *next_fit = NULL;
    106190
    107191/** Futex for thread-safe heap manipulation */
    108192static futex_t malloc_futex = FUTEX_INITIALIZER;
    109193
    110 /** Address of heap start */
    111 static void *heap_start = 0;
    112 
    113 /** Address of heap end */
    114 static void *heap_end = 0;
    115 
    116 /** Maximum heap size */
    117 static size_t max_heap_size = (size_t) -1;
    118 
    119 /** Current number of pages of heap area */
    120 static size_t heap_pages = 0;
     194#ifndef NDEBUG
     195
     196#define malloc_assert(expr) \
     197        do { \
     198                if (!(expr)) {\
     199                        futex_up(&malloc_futex); \
     200                        assert_abort(#expr, __FILE__, __LINE__); \
     201                } \
     202        } while (0)
     203
     204#else /* NDEBUG */
     205
     206#define malloc_assert(expr)
     207
     208#endif /* NDEBUG */
    121209
    122210/** Initialize a heap block
    123211 *
    124  * Fills in the structures related to a heap block.
     212 * Fill in the structures related to a heap block.
    125213 * Should be called only inside the critical section.
    126214 *
     
    128216 * @param size Size of the block including the header and the footer.
    129217 * @param free Indication of a free block.
    130  *
    131  */
    132 static void block_init(void *addr, size_t size, bool free)
     218 * @param area Heap area the block belongs to.
     219 *
     220 */
     221static void block_init(void *addr, size_t size, bool free, heap_area_t *area)
    133222{
    134223        /* Calculate the position of the header and the footer */
    135224        heap_block_head_t *head = (heap_block_head_t *) addr;
    136         heap_block_foot_t *foot =
    137             (heap_block_foot_t *) (addr + size - sizeof(heap_block_foot_t));
    138225       
    139226        head->size = size;
    140227        head->free = free;
     228        head->area = area;
    141229        head->magic = HEAP_BLOCK_HEAD_MAGIC;
     230       
     231        heap_block_foot_t *foot = BLOCK_FOOT(head);
    142232       
    143233        foot->size = size;
     
    158248        heap_block_head_t *head = (heap_block_head_t *) addr;
    159249       
    160         assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
    161        
    162         heap_block_foot_t *foot =
    163             (heap_block_foot_t *) (addr + head->size - sizeof(heap_block_foot_t));
    164        
    165         assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
    166         assert(head->size == foot->size);
    167 }
    168 
    169 /** Increase the heap area size
     250        malloc_assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
     251       
     252        heap_block_foot_t *foot = BLOCK_FOOT(head);
     253       
     254        malloc_assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
     255        malloc_assert(head->size == foot->size);
     256}
     257
     258/** Check a heap area structure
    170259 *
    171260 * Should be called only inside the critical section.
    172261 *
    173  * @param size Number of bytes to grow the heap by.
    174  *
    175  */
    176 static bool grow_heap(size_t size)
     262 * @param addr Address of the heap area.
     263 *
     264 */
     265static void area_check(void *addr)
     266{
     267        heap_area_t *area = (heap_area_t *) addr;
     268       
     269        malloc_assert(area->magic == HEAP_AREA_MAGIC);
     270        malloc_assert(addr == area->start);
     271        malloc_assert(area->start < area->end);
     272        malloc_assert(((uintptr_t) area->start % PAGE_SIZE) == 0);
     273        malloc_assert(((uintptr_t) area->end % PAGE_SIZE) == 0);
     274}
     275
     276/** Create new heap area
     277 *
     278 * Should be called only inside the critical section.
     279 *
     280 * @param size Size of the area.
     281 *
     282 */
     283static bool area_create(size_t size)
     284{
     285        void *start = as_get_mappable_page(size);
     286        if (start == NULL)
     287                return false;
     288       
     289        /* Align the heap area on page boundary */
     290        void *astart = (void *) ALIGN_UP((uintptr_t) start, PAGE_SIZE);
     291        size_t asize = ALIGN_UP(size, PAGE_SIZE);
     292       
     293        astart = as_area_create(astart, asize, AS_AREA_WRITE | AS_AREA_READ);
     294        if (astart == (void *) -1)
     295                return false;
     296       
     297        heap_area_t *area = (heap_area_t *) astart;
     298       
     299        area->start = astart;
     300        area->end = (void *) ((uintptr_t) astart + asize);
     301        area->prev = NULL;
     302        area->next = NULL;
     303        area->magic = HEAP_AREA_MAGIC;
     304       
     305        void *block = (void *) AREA_FIRST_BLOCK_HEAD(area);
     306        size_t bsize = (size_t) (area->end - block);
     307       
     308        block_init(block, bsize, true, area);
     309       
     310        if (last_heap_area == NULL) {
     311                first_heap_area = area;
     312                last_heap_area = area;
     313        } else {
     314                area->prev = last_heap_area;
     315                last_heap_area->next = area;
     316                last_heap_area = area;
     317        }
     318       
     319        return true;
     320}
     321
     322/** Try to enlarge a heap area
     323 *
     324 * Should be called only inside the critical section.
     325 *
     326 * @param area Heap area to grow.
     327 * @param size Gross size to grow (bytes).
     328 *
     329 * @return True if successful.
     330 *
     331 */
     332static bool area_grow(heap_area_t *area, size_t size)
    177333{
    178334        if (size == 0)
     335                return true;
     336       
     337        area_check(area);
     338       
     339        /* New heap area size */
     340        size_t gross_size = (size_t) (area->end - area->start) + size;
     341        size_t asize = ALIGN_UP(gross_size, PAGE_SIZE);
     342        void *end = (void *) ((uintptr_t) area->start + asize);
     343       
     344        /* Check for overflow */
     345        if (end < area->start)
    179346                return false;
    180 
    181         if ((heap_start + size < heap_start) || (heap_end + size < heap_end))
     347       
     348        /* Resize the address space area */
     349        int ret = as_area_resize(area->start, asize, 0);
     350        if (ret != EOK)
    182351                return false;
    183352       
    184         size_t heap_size = (size_t) (heap_end - heap_start);
    185        
    186         if ((max_heap_size != (size_t) -1) && (heap_size + size > max_heap_size))
    187                 return false;
    188        
    189         size_t pages = (size - 1) / PAGE_SIZE + 1;
    190        
    191         if (as_area_resize((void *) &_heap, (heap_pages + pages) * PAGE_SIZE, 0)
    192             == EOK) {
    193                 void *end = (void *) ALIGN_DOWN(((uintptr_t) &_heap) +
    194                     (heap_pages + pages) * PAGE_SIZE, BASE_ALIGN);
    195                 block_init(heap_end, end - heap_end, true);
    196                 heap_pages += pages;
    197                 heap_end = end;
     353        /* Add new free block */
     354        size_t net_size = (size_t) (end - area->end);
     355        if (net_size > 0)
     356                block_init(area->end, net_size, true, area);
     357       
     358        /* Update heap area parameters */
     359        area->end = end;
     360       
     361        return true;
     362}
     363
     364/** Try to enlarge any of the heap areas
     365 *
     366 * Should be called only inside the critical section.
     367 *
     368 * @param size Gross size of item to allocate (bytes).
     369 *
     370 */
     371static bool heap_grow(size_t size)
     372{
     373        if (size == 0)
    198374                return true;
    199         }
    200        
    201         return false;
    202 }
    203 
    204 /** Decrease the heap area
     375       
     376        /* First try to enlarge some existing area */
     377        for (heap_area_t *area = first_heap_area; area != NULL;
     378            area = area->next) {
     379                if (area_grow(area, size))
     380                        return true;
     381        }
     382       
     383        /* Eventually try to create a new area */
     384        return area_create(AREA_OVERHEAD(size));
     385}
     386
     387/** Try to shrink heap
    205388 *
    206389 * Should be called only inside the critical section.
    207  *
    208  * @param size Number of bytes to shrink the heap by.
    209  *
    210  */
    211 static void shrink_heap(void)
    212 {
    213         // TODO
     390 * In all cases the next pointer is reset.
     391 *
     392 * @param area Last modified heap area.
     393 *
     394 */
     395static void heap_shrink(heap_area_t *area)
     396{
     397        area_check(area);
     398       
     399        heap_block_foot_t *last_foot =
     400            (heap_block_foot_t *) AREA_LAST_BLOCK_FOOT(area);
     401        heap_block_head_t *last_head = BLOCK_HEAD(last_foot);
     402       
     403        block_check((void *) last_head);
     404        malloc_assert(last_head->area == area);
     405       
     406        if (last_head->free) {
     407                /*
     408                 * The last block of the heap area is
     409                 * unused. The area might be potentially
     410                 * shrunk.
     411                 */
     412               
     413                heap_block_head_t *first_head =
     414                    (heap_block_head_t *) AREA_FIRST_BLOCK_HEAD(area);
     415               
     416                block_check((void *) first_head);
     417                malloc_assert(first_head->area == area);
     418               
     419                size_t shrink_size = ALIGN_DOWN(last_head->size, PAGE_SIZE);
     420               
     421                if (first_head == last_head) {
     422                        /*
     423                         * The entire heap area consists of a single
     424                         * free heap block. This means we can get rid
     425                         * of it entirely.
     426                         */
     427                       
     428                        heap_area_t *prev = area->prev;
     429                        heap_area_t *next = area->next;
     430                       
     431                        if (prev != NULL) {
     432                                area_check(prev);
     433                                prev->next = next;
     434                        } else
     435                                first_heap_area = next;
     436                       
     437                        if (next != NULL) {
     438                                area_check(next);
     439                                next->prev = prev;
     440                        } else
     441                                last_heap_area = prev;
     442                       
     443                        as_area_destroy(area->start);
     444                } else if (shrink_size >= SHRINK_GRANULARITY) {
     445                        /*
     446                         * Make sure that we always shrink the area
     447                         * by a multiple of page size and update
     448                         * the block layout accordingly.
     449                         */
     450                       
     451                        size_t asize = (size_t) (area->end - area->start) - shrink_size;
     452                        void *end = (void *) ((uintptr_t) area->start + asize);
     453                       
     454                        /* Resize the address space area */
     455                        int ret = as_area_resize(area->start, asize, 0);
     456                        if (ret != EOK)
     457                                abort();
     458                       
     459                        /* Update heap area parameters */
     460                        area->end = end;
     461                        size_t excess = ((size_t) area->end) - ((size_t) last_head);
     462                       
     463                        if (excess > 0) {
     464                                if (excess >= STRUCT_OVERHEAD) {
     465                                        /*
     466                                         * The previous block cannot be free and there
     467                                         * is enough free space left in the area to
     468                                         * create a new free block.
     469                                         */
     470                                        block_init((void *) last_head, excess, true, area);
     471                                } else {
     472                                        /*
     473                                         * The excess is small. Therefore just enlarge
     474                                         * the previous block.
     475                                         */
     476                                        heap_block_foot_t *prev_foot = (heap_block_foot_t *)
     477                                            (((uintptr_t) last_head) - sizeof(heap_block_foot_t));
     478                                        heap_block_head_t *prev_head = BLOCK_HEAD(prev_foot);
     479                                       
     480                                        block_check((void *) prev_head);
     481                                       
     482                                        block_init(prev_head, prev_head->size + excess,
     483                                            prev_head->free, area);
     484                                }
     485                        }
     486                }
     487        }
     488       
     489        next_fit = NULL;
    214490}
    215491
     
    223499void __malloc_init(void)
    224500{
    225         if (!as_area_create((void *) &_heap, PAGE_SIZE,
    226             AS_AREA_WRITE | AS_AREA_READ))
     501        if (!area_create(PAGE_SIZE))
    227502                abort();
    228        
    229         heap_pages = 1;
    230         heap_start = (void *) ALIGN_UP((uintptr_t) &_heap, BASE_ALIGN);
    231         heap_end =
    232             (void *) ALIGN_DOWN(((uintptr_t) &_heap) + PAGE_SIZE, BASE_ALIGN);
    233        
    234         /* Make the entire area one large block. */
    235         block_init(heap_start, heap_end - heap_start, true);
    236 }
    237 
    238 /** Get maximum heap address
    239  *
    240  */
    241 uintptr_t get_max_heap_addr(void)
    242 {
    243         futex_down(&malloc_futex);
    244        
    245         if (max_heap_size == (size_t) -1)
    246                 max_heap_size =
    247                     max((size_t) (heap_end - heap_start), MAX_HEAP_SIZE);
    248        
    249         uintptr_t max_heap_addr = (uintptr_t) heap_start + max_heap_size;
    250        
    251         futex_up(&malloc_futex);
    252        
    253         return max_heap_addr;
    254503}
    255504
     
    265514static void split_mark(heap_block_head_t *cur, const size_t size)
    266515{
    267         assert(cur->size >= size);
     516        malloc_assert(cur->size >= size);
    268517       
    269518        /* See if we should split the block. */
     
    273522                /* Block big enough -> split. */
    274523                void *next = ((void *) cur) + size;
    275                 block_init(next, cur->size - size, true);
    276                 block_init(cur, size, false);
     524                block_init(next, cur->size - size, true, cur->area);
     525                block_init(cur, size, false, cur->area);
    277526        } else {
    278527                /* Block too small -> use as is. */
     
    281530}
    282531
    283 /** Allocate a memory block
     532/** Allocate memory from heap area starting from given block
    284533 *
    285534 * Should be called only inside the critical section.
    286  *
    287  * @param size  The size of the block to allocate.
    288  * @param align Memory address alignment.
    289  *
    290  * @return the address of the block or NULL when not enough memory.
    291  *
    292  */
    293 static void *malloc_internal(const size_t size, const size_t align)
    294 {
    295         if (align == 0)
    296                 return NULL;
    297        
    298         size_t falign = lcm(align, BASE_ALIGN);
    299         size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign));
    300        
    301         bool grown = false;
    302         void *result;
    303        
    304 loop:
    305         result = NULL;
    306         heap_block_head_t *cur = (heap_block_head_t *) heap_start;
    307        
    308         while ((result == NULL) && ((void *) cur < heap_end)) {
     535 * As a side effect this function also sets the current
     536 * pointer on successful allocation.
     537 *
     538 * @param area        Heap area where to allocate from.
     539 * @param first_block Starting heap block.
     540 * @param final_block Heap block where to finish the search
     541 *                    (may be NULL).
     542 * @param real_size   Gross number of bytes to allocate.
     543 * @param falign      Physical alignment of the block.
     544 *
     545 * @return Address of the allocated block or NULL on not enough memory.
     546 *
     547 */
     548static void *malloc_area(heap_area_t *area, heap_block_head_t *first_block,
     549    heap_block_head_t *final_block, size_t real_size, size_t falign)
     550{
     551        area_check((void *) area);
     552        malloc_assert((void *) first_block >= (void *) AREA_FIRST_BLOCK_HEAD(area));
     553        malloc_assert((void *) first_block < area->end);
     554       
     555        for (heap_block_head_t *cur = first_block; (void *) cur < area->end;
     556            cur = (heap_block_head_t *) (((void *) cur) + cur->size)) {
    309557                block_check(cur);
     558               
     559                /* Finish searching on the final block */
     560                if ((final_block != NULL) && (cur == final_block))
     561                        break;
    310562               
    311563                /* Try to find a block that is free and large enough. */
    312564                if ((cur->free) && (cur->size >= real_size)) {
    313                         /* We have found a suitable block.
    314                            Check for alignment properties. */
    315                         void *addr = ((void *) cur) + sizeof(heap_block_head_t);
    316                         void *aligned = (void *) ALIGN_UP(addr, falign);
     565                        /*
     566                         * We have found a suitable block.
     567                         * Check for alignment properties.
     568                         */
     569                        void *addr = (void *)
     570                            ((uintptr_t) cur + sizeof(heap_block_head_t));
     571                        void *aligned = (void *)
     572                            ALIGN_UP((uintptr_t) addr, falign);
    317573                       
    318574                        if (addr == aligned) {
    319575                                /* Exact block start including alignment. */
    320576                                split_mark(cur, real_size);
    321                                 result = addr;
     577                               
     578                                next_fit = cur;
     579                                return addr;
    322580                        } else {
    323581                                /* Block start has to be aligned */
     
    325583                               
    326584                                if (cur->size >= real_size + excess) {
    327                                         /* The current block is large enough to fit
    328                                            data in including alignment */
    329                                         if ((void *) cur > heap_start) {
    330                                                 /* There is a block before the current block.
    331                                                    This previous block can be enlarged to compensate
    332                                                    for the alignment excess */
    333                                                 heap_block_foot_t *prev_foot =
    334                                                     ((void *) cur) - sizeof(heap_block_foot_t);
     585                                        /*
     586                                         * The current block is large enough to fit
     587                                         * data in (including alignment).
     588                                         */
     589                                        if ((void *) cur > (void *) AREA_FIRST_BLOCK_HEAD(area)) {
     590                                                /*
     591                                                 * There is a block before the current block.
     592                                                 * This previous block can be enlarged to
     593                                                 * compensate for the alignment excess.
     594                                                 */
     595                                                heap_block_foot_t *prev_foot = (heap_block_foot_t *)
     596                                                    ((void *) cur - sizeof(heap_block_foot_t));
    335597                                               
    336                                                 heap_block_head_t *prev_head =
    337                                                     (heap_block_head_t *) (((void *) cur) - prev_foot->size);
     598                                                heap_block_head_t *prev_head = (heap_block_head_t *)
     599                                                    ((void *) cur - prev_foot->size);
    338600                                               
    339601                                                block_check(prev_head);
     
    342604                                                heap_block_head_t *next_head = ((void *) cur) + excess;
    343605                                               
    344                                                 if ((!prev_head->free) && (excess >= STRUCT_OVERHEAD)) {
    345                                                         /* The previous block is not free and there is enough
    346                                                            space to fill in a new free block between the previous
    347                                                            and current block */
    348                                                         block_init(cur, excess, true);
     606                                                if ((!prev_head->free) &&
     607                                                    (excess >= STRUCT_OVERHEAD)) {
     608                                                        /*
     609                                                         * The previous block is not free and there
     610                                                         * is enough free space left to fill in
     611                                                         * a new free block between the previous
     612                                                         * and current block.
     613                                                         */
     614                                                        block_init(cur, excess, true, area);
    349615                                                } else {
    350                                                         /* The previous block is free (thus there is no need to
    351                                                            induce additional fragmentation to the heap) or the
    352                                                            excess is small, thus just enlarge the previous block */
    353                                                         block_init(prev_head, prev_head->size + excess, prev_head->free);
     616                                                        /*
     617                                                         * The previous block is free (thus there
     618                                                         * is no need to induce additional
     619                                                         * fragmentation to the heap) or the
     620                                                         * excess is small. Therefore just enlarge
     621                                                         * the previous block.
     622                                                         */
     623                                                        block_init(prev_head, prev_head->size + excess,
     624                                                            prev_head->free, area);
    354625                                                }
    355626                                               
    356                                                 block_init(next_head, reduced_size, true);
     627                                                block_init(next_head, reduced_size, true, area);
    357628                                                split_mark(next_head, real_size);
    358                                                 result = aligned;
    359                                                 cur = next_head;
     629                                               
     630                                                next_fit = next_head;
     631                                                return aligned;
    360632                                        } else {
    361                                                 /* The current block is the first block on the heap.
    362                                                    We have to make sure that the alignment excess
    363                                                    is large enough to fit a new free block just
    364                                                    before the current block */
     633                                                /*
     634                                                 * The current block is the first block
     635                                                 * in the heap area. We have to make sure
     636                                                 * that the alignment excess is large enough
     637                                                 * to fit a new free block just before the
     638                                                 * current block.
     639                                                 */
    365640                                                while (excess < STRUCT_OVERHEAD) {
    366641                                                        aligned += falign;
     
    371646                                                if (cur->size >= real_size + excess) {
    372647                                                        size_t reduced_size = cur->size - excess;
    373                                                         cur = (heap_block_head_t *) (heap_start + excess);
     648                                                        cur = (heap_block_head_t *)
     649                                                            (AREA_FIRST_BLOCK_HEAD(area) + excess);
    374650                                                       
    375                                                         block_init(heap_start, excess, true);
    376                                                         block_init(cur, reduced_size, true);
     651                                                        block_init((void *) AREA_FIRST_BLOCK_HEAD(area),
     652                                                            excess, true, area);
     653                                                        block_init(cur, reduced_size, true, area);
    377654                                                        split_mark(cur, real_size);
    378                                                         result = aligned;
     655                                                       
     656                                                        next_fit = cur;
     657                                                        return aligned;
    379658                                                }
    380659                                        }
     
    382661                        }
    383662                }
    384                
    385                 /* Advance to the next block. */
    386                 cur = (heap_block_head_t *) (((void *) cur) + cur->size);
    387         }
    388        
    389         if ((result == NULL) && (!grown)) {
    390                 if (grow_heap(real_size)) {
    391                         grown = true;
     663        }
     664       
     665        return NULL;
     666}
     667
     668/** Allocate a memory block
     669 *
     670 * Should be called only inside the critical section.
     671 *
     672 * @param size  The size of the block to allocate.
     673 * @param align Memory address alignment.
     674 *
     675 * @return Address of the allocated block or NULL on not enough memory.
     676 *
     677 */
     678static void *malloc_internal(const size_t size, const size_t align)
     679{
     680        malloc_assert(first_heap_area != NULL);
     681       
     682        if (align == 0)
     683                return NULL;
     684       
     685        size_t falign = lcm(align, BASE_ALIGN);
     686        size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign));
     687       
     688        bool retry = false;
     689        heap_block_head_t *split;
     690       
     691loop:
     692       
     693        /* Try the next fit approach */
     694        split = next_fit;
     695       
     696        if (split != NULL) {
     697                void *addr = malloc_area(split->area, split, NULL, real_size,
     698                    falign);
     699               
     700                if (addr != NULL)
     701                        return addr;
     702        }
     703       
     704        /* Search the entire heap */
     705        for (heap_area_t *area = first_heap_area; area != NULL;
     706            area = area->next) {
     707                heap_block_head_t *first = (heap_block_head_t *)
     708                    AREA_FIRST_BLOCK_HEAD(area);
     709               
     710                void *addr = malloc_area(area, first, split, real_size,
     711                    falign);
     712               
     713                if (addr != NULL)
     714                        return addr;
     715        }
     716       
     717        if (!retry) {
     718                /* Try to grow the heap space */
     719                if (heap_grow(real_size)) {
     720                        retry = true;
    392721                        goto loop;
    393722                }
    394723        }
    395724       
    396         return result;
     725        return NULL;
    397726}
    398727
     
    473802            (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
    474803       
    475         assert((void *) head >= heap_start);
    476         assert((void *) head < heap_end);
    477        
    478804        block_check(head);
    479         assert(!head->free);
     805        malloc_assert(!head->free);
     806       
     807        heap_area_t *area = head->area;
     808       
     809        area_check(area);
     810        malloc_assert((void *) head >= (void *) AREA_FIRST_BLOCK_HEAD(area));
     811        malloc_assert((void *) head < area->end);
    480812       
    481813        void *ptr = NULL;
     
    487819                /* Shrink */
    488820                if (orig_size - real_size >= STRUCT_OVERHEAD) {
    489                         /* Split the original block to a full block
    490                            and a trailing free block */
    491                         block_init((void *) head, real_size, false);
     821                        /*
     822                         * Split the original block to a full block
     823                         * and a trailing free block.
     824                         */
     825                        block_init((void *) head, real_size, false, area);
    492826                        block_init((void *) head + real_size,
    493                             orig_size - real_size, true);
    494                         shrink_heap();
     827                            orig_size - real_size, true, area);
     828                        heap_shrink(area);
    495829                }
    496830               
    497831                ptr = ((void *) head) + sizeof(heap_block_head_t);
    498832        } else {
    499                 /* Look at the next block. If it is free and the size is
    500                    sufficient then merge the two. Otherwise just allocate
    501                    a new block, copy the original data into it and
    502                    free the original block. */
     833                /*
     834                 * Look at the next block. If it is free and the size is
     835                 * sufficient then merge the two. Otherwise just allocate
     836                 * a new block, copy the original data into it and
     837                 * free the original block.
     838                 */
    503839                heap_block_head_t *next_head =
    504840                    (heap_block_head_t *) (((void *) head) + head->size);
    505841               
    506                 if (((void *) next_head < heap_end) &&
     842                if (((void *) next_head < area->end) &&
    507843                    (head->size + next_head->size >= real_size) &&
    508844                    (next_head->free)) {
    509845                        block_check(next_head);
    510                         block_init(head, head->size + next_head->size, false);
     846                        block_init(head, head->size + next_head->size, false, area);
    511847                        split_mark(head, real_size);
    512848                       
    513849                        ptr = ((void *) head) + sizeof(heap_block_head_t);
     850                        next_fit = NULL;
    514851                } else
    515852                        reloc = true;
     
    542879            = (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
    543880       
    544         assert((void *) head >= heap_start);
    545         assert((void *) head < heap_end);
    546        
    547881        block_check(head);
    548         assert(!head->free);
     882        malloc_assert(!head->free);
     883       
     884        heap_area_t *area = head->area;
     885       
     886        area_check(area);
     887        malloc_assert((void *) head >= (void *) AREA_FIRST_BLOCK_HEAD(area));
     888        malloc_assert((void *) head < area->end);
    549889       
    550890        /* Mark the block itself as free. */
     
    555895            = (heap_block_head_t *) (((void *) head) + head->size);
    556896       
    557         if ((void *) next_head < heap_end) {
     897        if ((void *) next_head < area->end) {
    558898                block_check(next_head);
    559899                if (next_head->free)
    560                         block_init(head, head->size + next_head->size, true);
     900                        block_init(head, head->size + next_head->size, true, area);
    561901        }
    562902       
    563903        /* Look at the previous block. If it is free, merge the two. */
    564         if ((void *) head > heap_start) {
     904        if ((void *) head > (void *) AREA_FIRST_BLOCK_HEAD(area)) {
    565905                heap_block_foot_t *prev_foot =
    566906                    (heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t));
     
    572912               
    573913                if (prev_head->free)
    574                         block_init(prev_head, prev_head->size + head->size, true);
    575         }
    576        
    577         shrink_heap();
     914                        block_init(prev_head, prev_head->size + head->size, true,
     915                            area);
     916        }
     917       
     918        heap_shrink(area);
    578919       
    579920        futex_up(&malloc_futex);
    580921}
    581922
     923void *heap_check(void)
     924{
     925        futex_down(&malloc_futex);
     926       
     927        if (first_heap_area == NULL) {
     928                futex_up(&malloc_futex);
     929                return (void *) -1;
     930        }
     931       
     932        /* Walk all heap areas */
     933        for (heap_area_t *area = first_heap_area; area != NULL;
     934            area = area->next) {
     935               
     936                /* Check heap area consistency */
     937                if ((area->magic != HEAP_AREA_MAGIC) ||
     938                    ((void *) area != area->start) ||
     939                    (area->start >= area->end) ||
     940                    (((uintptr_t) area->start % PAGE_SIZE) != 0) ||
     941                    (((uintptr_t) area->end % PAGE_SIZE) != 0)) {
     942                        futex_up(&malloc_futex);
     943                        return (void *) area;
     944                }
     945               
     946                /* Walk all heap blocks */
     947                for (heap_block_head_t *head = (heap_block_head_t *)
     948                    AREA_FIRST_BLOCK_HEAD(area); (void *) head < area->end;
     949                    head = (heap_block_head_t *) (((void *) head) + head->size)) {
     950                       
     951                        /* Check heap block consistency */
     952                        if (head->magic != HEAP_BLOCK_HEAD_MAGIC) {
     953                                futex_up(&malloc_futex);
     954                                return (void *) head;
     955                        }
     956                       
     957                        heap_block_foot_t *foot = BLOCK_FOOT(head);
     958                       
     959                        if ((foot->magic != HEAP_BLOCK_FOOT_MAGIC) ||
     960                            (head->size != foot->size)) {
     961                                futex_up(&malloc_futex);
     962                                return (void *) foot;
     963                        }
     964                }
     965        }
     966       
     967        futex_up(&malloc_futex);
     968       
     969        return NULL;
     970}
     971
    582972/** @}
    583973 */
Note: See TracChangeset for help on using the changeset viewer.