source: mainline/uspace/lib/c/generic/malloc.c@ fdfb24e

topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fdfb24e was 44e8541, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 20 months ago

Move stdlib.h and some of its function into /common

  • Property mode set to 100644
File size: 25.6 KB
RevLine 
[6db6fd1]1/*
2 * Copyright (c) 2009 Martin Decky
3 * Copyright (c) 2009 Petr Tuma
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libc
31 * @{
32 */
33/** @file
34 */
35
[44e8541]36#include <stdlib.h>
[ffccdff0]37#include <stdalign.h>
[3e6a98c5]38#include <stdbool.h>
[582a0b8]39#include <stddef.h>
[6db6fd1]40#include <as.h>
41#include <align.h>
42#include <macros.h>
43#include <assert.h>
44#include <errno.h>
45#include <bitops.h>
46#include <mem.h>
[d161715]47#include <stdlib.h>
[6db6fd1]48#include <adt/gcdlcm.h>
[44e8541]49#include <malloc.h>
[f787c8e]50
[47b7006]51#include "private/malloc.h"
[f787c8e]52#include "private/fibril.h"
[6db6fd1]53
[0b37882]54/** Magic used in heap headers. */
55#define HEAP_BLOCK_HEAD_MAGIC UINT32_C(0xBEEF0101)
[6db6fd1]56
[0b37882]57/** Magic used in heap footers. */
58#define HEAP_BLOCK_FOOT_MAGIC UINT32_C(0xBEEF0202)
[6db6fd1]59
[0b37882]60/** Magic used in heap descriptor. */
61#define HEAP_AREA_MAGIC UINT32_C(0xBEEFCAFE)
62
63/** Allocation alignment.
64 *
65 * This also covers the alignment of fields
66 * in the heap header and footer.
67 *
68 */
[6db6fd1]69#define BASE_ALIGN 16
70
[013a5d7]71/** Heap shrink granularity
72 *
[21799398]73 * Try not to pump and stress the heap too much
[013a5d7]74 * by shrinking and enlarging it too often.
[21799398]75 * A heap area won't shrink if the released
[013a5d7]76 * free block is smaller than this constant.
77 *
78 */
79#define SHRINK_GRANULARITY (64 * PAGE_SIZE)
80
[0b37882]81/** Overhead of each heap block. */
82#define STRUCT_OVERHEAD \
83 (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
84
85/** Calculate real size of a heap block.
86 *
87 * Add header and footer size.
88 *
[6db6fd1]89 */
[0b37882]90#define GROSS_SIZE(size) ((size) + STRUCT_OVERHEAD)
[6db6fd1]91
[0b37882]92/** Calculate net size of a heap block.
93 *
94 * Subtract header and footer size.
[6db6fd1]95 *
96 */
[0b37882]97#define NET_SIZE(size) ((size) - STRUCT_OVERHEAD)
[6db6fd1]98
[94e75cf]99/** Overhead of each area. */
100#define AREA_OVERHEAD(size) \
101 (ALIGN_UP(GROSS_SIZE(size) + sizeof(heap_area_t), BASE_ALIGN))
102
[0b37882]103/** Get first block in heap area.
104 *
[6db6fd1]105 */
[013a5d7]106#define AREA_FIRST_BLOCK_HEAD(area) \
[0b37882]107 (ALIGN_UP(((uintptr_t) (area)) + sizeof(heap_area_t), BASE_ALIGN))
[6db6fd1]108
[013a5d7]109/** Get last block in heap area.
110 *
111 */
112#define AREA_LAST_BLOCK_FOOT(area) \
113 (((uintptr_t) (area)->end) - sizeof(heap_block_foot_t))
114
[7aafdb86]115#define AREA_LAST_BLOCK_HEAD(area) \
[da287d1]116 ((uintptr_t) BLOCK_HEAD(((heap_block_foot_t *) AREA_LAST_BLOCK_FOOT(area))))
[7aafdb86]117
[013a5d7]118/** Get header in heap block.
119 *
120 */
121#define BLOCK_HEAD(foot) \
122 ((heap_block_head_t *) \
123 (((uintptr_t) (foot)) + sizeof(heap_block_foot_t) - (foot)->size))
124
[0b37882]125/** Get footer in heap block.
126 *
[6db6fd1]127 */
[0b37882]128#define BLOCK_FOOT(head) \
129 ((heap_block_foot_t *) \
[013a5d7]130 (((uintptr_t) (head)) + (head)->size - sizeof(heap_block_foot_t)))
[0b37882]131
132/** Heap area.
133 *
134 * The memory managed by the heap allocator is divided into
135 * multiple discontinuous heaps. Each heap is represented
136 * by a separate address space area which has this structure
137 * at its very beginning.
138 *
139 */
140typedef struct heap_area {
141 /** Start of the heap area (including this structure)
142 *
143 * Aligned on page boundary.
144 *
145 */
146 void *start;
[a35b458]147
[0b37882]148 /** End of the heap area (aligned on page boundary) */
149 void *end;
[a35b458]150
[013a5d7]151 /** Previous heap area */
152 struct heap_area *prev;
[a35b458]153
[0b37882]154 /** Next heap area */
155 struct heap_area *next;
[a35b458]156
[0b37882]157 /** A magic value */
158 uint32_t magic;
159} heap_area_t;
[6db6fd1]160
161/** Header of a heap block
162 *
163 */
164typedef struct {
165 /* Size of the block (including header and footer) */
166 size_t size;
[a35b458]167
[6db6fd1]168 /* Indication of a free block */
169 bool free;
[a35b458]170
[0b37882]171 /** Heap area this block belongs to */
172 heap_area_t *area;
[a35b458]173
[6db6fd1]174 /* A magic value to detect overwrite of heap header */
175 uint32_t magic;
176} heap_block_head_t;
177
178/** Footer of a heap block
179 *
180 */
181typedef struct {
182 /* Size of the block (including header and footer) */
183 size_t size;
[a35b458]184
[6db6fd1]185 /* A magic value to detect overwrite of heap footer */
186 uint32_t magic;
187} heap_block_foot_t;
188
[0b37882]189/** First heap area */
190static heap_area_t *first_heap_area = NULL;
[6db6fd1]191
[0b37882]192/** Last heap area */
193static heap_area_t *last_heap_area = NULL;
[6db6fd1]194
[0b37882]195/** Next heap block to examine (next fit algorithm) */
[207533f]196static heap_block_head_t *next_fit = NULL;
[6db6fd1]197
[0b37882]198/** Futex for thread-safe heap manipulation */
[45c8eea]199static fibril_rmutex_t malloc_mutex;
[6db6fd1]200
[e8d3c6f5]201#define malloc_assert(expr) safe_assert(expr)
[af5dfa5b]202
[ffccdff0]203/*
204 * Make sure the base alignment is sufficient.
205 */
206static_assert(BASE_ALIGN >= alignof(heap_area_t), "");
207static_assert(BASE_ALIGN >= alignof(heap_block_head_t), "");
208static_assert(BASE_ALIGN >= alignof(heap_block_foot_t), "");
209static_assert(BASE_ALIGN >= alignof(max_align_t), "");
210
[af5dfa5b]211/** Serializes access to the heap from multiple threads. */
212static inline void heap_lock(void)
213{
[9bde0d5]214 fibril_rmutex_lock(&malloc_mutex);
[af5dfa5b]215}
216
217/** Serializes access to the heap from multiple threads. */
218static inline void heap_unlock(void)
219{
[9bde0d5]220 fibril_rmutex_unlock(&malloc_mutex);
[af5dfa5b]221}
222
[6db6fd1]223/** Initialize a heap block
224 *
[0b37882]225 * Fill in the structures related to a heap block.
[3292623]226 * Should be called only inside the critical section.
[6db6fd1]227 *
228 * @param addr Address of the block.
229 * @param size Size of the block including the header and the footer.
230 * @param free Indication of a free block.
[0b37882]231 * @param area Heap area the block belongs to.
[6db6fd1]232 *
233 */
[0b37882]234static void block_init(void *addr, size_t size, bool free, heap_area_t *area)
[6db6fd1]235{
236 /* Calculate the position of the header and the footer */
237 heap_block_head_t *head = (heap_block_head_t *) addr;
[a35b458]238
[6db6fd1]239 head->size = size;
240 head->free = free;
[0b37882]241 head->area = area;
[6db6fd1]242 head->magic = HEAP_BLOCK_HEAD_MAGIC;
[a35b458]243
[0b37882]244 heap_block_foot_t *foot = BLOCK_FOOT(head);
[a35b458]245
[6db6fd1]246 foot->size = size;
247 foot->magic = HEAP_BLOCK_FOOT_MAGIC;
248}
249
250/** Check a heap block
251 *
252 * Verifies that the structures related to a heap block still contain
253 * the magic constants. This helps detect heap corruption early on.
[3292623]254 * Should be called only inside the critical section.
[6db6fd1]255 *
256 * @param addr Address of the block.
257 *
258 */
259static void block_check(void *addr)
260{
261 heap_block_head_t *head = (heap_block_head_t *) addr;
[a35b458]262
[13f2461]263 malloc_assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
[a35b458]264
[0b37882]265 heap_block_foot_t *foot = BLOCK_FOOT(head);
[a35b458]266
[13f2461]267 malloc_assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
268 malloc_assert(head->size == foot->size);
[6db6fd1]269}
270
[0b37882]271/** Check a heap area structure
[013a5d7]272 *
273 * Should be called only inside the critical section.
[3292623]274 *
[0b37882]275 * @param addr Address of the heap area.
[3292623]276 *
[0b37882]277 */
278static void area_check(void *addr)
279{
280 heap_area_t *area = (heap_area_t *) addr;
[a35b458]281
[13f2461]282 malloc_assert(area->magic == HEAP_AREA_MAGIC);
283 malloc_assert(addr == area->start);
284 malloc_assert(area->start < area->end);
285 malloc_assert(((uintptr_t) area->start % PAGE_SIZE) == 0);
286 malloc_assert(((uintptr_t) area->end % PAGE_SIZE) == 0);
[0b37882]287}
288
289/** Create new heap area
290 *
[013a5d7]291 * Should be called only inside the critical section.
292 *
293 * @param size Size of the area.
[3292623]294 *
295 */
[0b37882]296static bool area_create(size_t size)
[6db6fd1]297{
[fbcdeb8]298 /* Align the heap area size on page boundary */
[0b37882]299 size_t asize = ALIGN_UP(size, PAGE_SIZE);
[faba839]300 void *astart = as_area_create(AS_AREA_ANY, asize,
[6aeca0d]301 AS_AREA_WRITE | AS_AREA_READ | AS_AREA_CACHEABLE, AS_AREA_UNPAGED);
[faba839]302 if (astart == AS_MAP_FAILED)
[e70edd1]303 return false;
[a35b458]304
[0b37882]305 heap_area_t *area = (heap_area_t *) astart;
[a35b458]306
[0b37882]307 area->start = astart;
[013a5d7]308 area->end = (void *) ((uintptr_t) astart + asize);
309 area->prev = NULL;
[0b37882]310 area->next = NULL;
311 area->magic = HEAP_AREA_MAGIC;
[a35b458]312
[013a5d7]313 void *block = (void *) AREA_FIRST_BLOCK_HEAD(area);
[0b37882]314 size_t bsize = (size_t) (area->end - block);
[a35b458]315
[0b37882]316 block_init(block, bsize, true, area);
[a35b458]317
[0b37882]318 if (last_heap_area == NULL) {
319 first_heap_area = area;
320 last_heap_area = area;
321 } else {
[013a5d7]322 area->prev = last_heap_area;
[0b37882]323 last_heap_area->next = area;
324 last_heap_area = area;
325 }
[a35b458]326
[0b37882]327 return true;
328}
329
330/** Try to enlarge a heap area
[013a5d7]331 *
332 * Should be called only inside the critical section.
[0b37882]333 *
334 * @param area Heap area to grow.
[013a5d7]335 * @param size Gross size to grow (bytes).
336 *
337 * @return True if successful.
[0b37882]338 *
339 */
340static bool area_grow(heap_area_t *area, size_t size)
341{
342 if (size == 0)
343 return true;
[a35b458]344
[0b37882]345 area_check(area);
[a35b458]346
[0b37882]347 /* New heap area size */
[013a5d7]348 size_t gross_size = (size_t) (area->end - area->start) + size;
349 size_t asize = ALIGN_UP(gross_size, PAGE_SIZE);
350 void *end = (void *) ((uintptr_t) area->start + asize);
[a35b458]351
[0b37882]352 /* Check for overflow */
353 if (end < area->start)
354 return false;
[a35b458]355
[0b37882]356 /* Resize the address space area */
[b7fd2a0]357 errno_t ret = as_area_resize(area->start, asize, 0);
[0b37882]358 if (ret != EOK)
[6db6fd1]359 return false;
[a35b458]360
[da287d1]361 heap_block_head_t *last_head =
362 (heap_block_head_t *) AREA_LAST_BLOCK_HEAD(area);
[a35b458]363
[7aafdb86]364 if (last_head->free) {
365 /* Add the new space to the last block. */
366 size_t net_size = (size_t) (end - area->end) + last_head->size;
367 malloc_assert(net_size > 0);
368 block_init(last_head, net_size, true, area);
369 } else {
370 /* Add new free block */
371 size_t net_size = (size_t) (end - area->end);
372 if (net_size > 0)
373 block_init(area->end, net_size, true, area);
374 }
[a35b458]375
[0b37882]376 /* Update heap area parameters */
377 area->end = end;
[a35b458]378
[0b37882]379 return true;
380}
381
[013a5d7]382/** Try to shrink heap
[3292623]383 *
[013a5d7]384 * Should be called only inside the critical section.
[0b37882]385 * In all cases the next pointer is reset.
[3292623]386 *
[013a5d7]387 * @param area Last modified heap area.
388 *
[3292623]389 */
[013a5d7]390static void heap_shrink(heap_area_t *area)
[6db6fd1]391{
[013a5d7]392 area_check(area);
[a35b458]393
[013a5d7]394 heap_block_foot_t *last_foot =
395 (heap_block_foot_t *) AREA_LAST_BLOCK_FOOT(area);
396 heap_block_head_t *last_head = BLOCK_HEAD(last_foot);
[a35b458]397
[013a5d7]398 block_check((void *) last_head);
[13f2461]399 malloc_assert(last_head->area == area);
[a35b458]400
[013a5d7]401 if (last_head->free) {
402 /*
403 * The last block of the heap area is
404 * unused. The area might be potentially
405 * shrunk.
406 */
[a35b458]407
[013a5d7]408 heap_block_head_t *first_head =
409 (heap_block_head_t *) AREA_FIRST_BLOCK_HEAD(area);
[a35b458]410
[013a5d7]411 block_check((void *) first_head);
[13f2461]412 malloc_assert(first_head->area == area);
[a35b458]413
[e6eee2b]414 size_t shrink_size = ALIGN_DOWN(last_head->size, PAGE_SIZE);
[a35b458]415
[013a5d7]416 if (first_head == last_head) {
417 /*
418 * The entire heap area consists of a single
419 * free heap block. This means we can get rid
420 * of it entirely.
421 */
[a35b458]422
[013a5d7]423 heap_area_t *prev = area->prev;
424 heap_area_t *next = area->next;
[a35b458]425
[013a5d7]426 if (prev != NULL) {
427 area_check(prev);
428 prev->next = next;
429 } else
430 first_heap_area = next;
[a35b458]431
[013a5d7]432 if (next != NULL) {
433 area_check(next);
434 next->prev = prev;
435 } else
436 last_heap_area = prev;
[a35b458]437
[013a5d7]438 as_area_destroy(area->start);
[e6eee2b]439 } else if (shrink_size >= SHRINK_GRANULARITY) {
[013a5d7]440 /*
441 * Make sure that we always shrink the area
442 * by a multiple of page size and update
443 * the block layout accordingly.
444 */
[a35b458]445
[013a5d7]446 size_t asize = (size_t) (area->end - area->start) - shrink_size;
447 void *end = (void *) ((uintptr_t) area->start + asize);
[a35b458]448
[013a5d7]449 /* Resize the address space area */
[b7fd2a0]450 errno_t ret = as_area_resize(area->start, asize, 0);
[013a5d7]451 if (ret != EOK)
452 abort();
[a35b458]453
[013a5d7]454 /* Update heap area parameters */
455 area->end = end;
[207533f]456 size_t excess = ((size_t) area->end) - ((size_t) last_head);
[a35b458]457
[013a5d7]458 if (excess > 0) {
459 if (excess >= STRUCT_OVERHEAD) {
460 /*
461 * The previous block cannot be free and there
462 * is enough free space left in the area to
463 * create a new free block.
464 */
[207533f]465 block_init((void *) last_head, excess, true, area);
[013a5d7]466 } else {
467 /*
468 * The excess is small. Therefore just enlarge
469 * the previous block.
470 */
471 heap_block_foot_t *prev_foot = (heap_block_foot_t *)
472 (((uintptr_t) last_head) - sizeof(heap_block_foot_t));
473 heap_block_head_t *prev_head = BLOCK_HEAD(prev_foot);
[a35b458]474
[013a5d7]475 block_check((void *) prev_head);
[a35b458]476
[013a5d7]477 block_init(prev_head, prev_head->size + excess,
478 prev_head->free, area);
479 }
480 }
481 }
482 }
[a35b458]483
[207533f]484 next_fit = NULL;
[6db6fd1]485}
486
487/** Initialize the heap allocator
488 *
[47b7006]489 * Create initial heap memory area. This routine is
490 * only called from libc initialization, thus we do not
491 * take any locks.
[6db6fd1]492 *
493 */
[47b7006]494void __malloc_init(void)
[6db6fd1]495{
[45c8eea]496 if (fibril_rmutex_initialize(&malloc_mutex) != EOK)
497 abort();
498
[0b37882]499 if (!area_create(PAGE_SIZE))
[47b7006]500 abort();
[6db6fd1]501}
502
[25f6bddb]503void __malloc_fini(void)
504{
505 fibril_rmutex_destroy(&malloc_mutex);
506}
507
[3292623]508/** Split heap block and mark it as used.
509 *
510 * Should be called only inside the critical section.
511 *
512 * @param cur Heap block to split.
513 * @param size Number of bytes to split and mark from the beginning
514 * of the block.
515 *
516 */
[6db6fd1]517static void split_mark(heap_block_head_t *cur, const size_t size)
518{
[13f2461]519 malloc_assert(cur->size >= size);
[a35b458]520
[6db6fd1]521 /* See if we should split the block. */
522 size_t split_limit = GROSS_SIZE(size);
[a35b458]523
[6db6fd1]524 if (cur->size > split_limit) {
525 /* Block big enough -> split. */
526 void *next = ((void *) cur) + size;
[0b37882]527 block_init(next, cur->size - size, true, cur->area);
528 block_init(cur, size, false, cur->area);
[6db6fd1]529 } else {
530 /* Block too small -> use as is. */
531 cur->free = false;
532 }
533}
534
[0b37882]535/** Allocate memory from heap area starting from given block
[3292623]536 *
537 * Should be called only inside the critical section.
[0b37882]538 * As a side effect this function also sets the current
539 * pointer on successful allocation.
[6db6fd1]540 *
[0b37882]541 * @param area Heap area where to allocate from.
542 * @param first_block Starting heap block.
543 * @param final_block Heap block where to finish the search
544 * (may be NULL).
545 * @param real_size Gross number of bytes to allocate.
546 * @param falign Physical alignment of the block.
[6db6fd1]547 *
[0b37882]548 * @return Address of the allocated block or NULL on not enough memory.
[6db6fd1]549 *
550 */
[0b37882]551static void *malloc_area(heap_area_t *area, heap_block_head_t *first_block,
552 heap_block_head_t *final_block, size_t real_size, size_t falign)
[6db6fd1]553{
[0b37882]554 area_check((void *) area);
[13f2461]555 malloc_assert((void *) first_block >= (void *) AREA_FIRST_BLOCK_HEAD(area));
556 malloc_assert((void *) first_block < area->end);
[a35b458]557
[013a5d7]558 for (heap_block_head_t *cur = first_block; (void *) cur < area->end;
[0b37882]559 cur = (heap_block_head_t *) (((void *) cur) + cur->size)) {
[6db6fd1]560 block_check(cur);
[a35b458]561
[0b37882]562 /* Finish searching on the final block */
563 if ((final_block != NULL) && (cur == final_block))
564 break;
[a35b458]565
[6db6fd1]566 /* Try to find a block that is free and large enough. */
567 if ((cur->free) && (cur->size >= real_size)) {
[0b37882]568 /*
569 * We have found a suitable block.
570 * Check for alignment properties.
571 */
572 void *addr = (void *)
573 ((uintptr_t) cur + sizeof(heap_block_head_t));
574 void *aligned = (void *)
575 ALIGN_UP((uintptr_t) addr, falign);
[a35b458]576
[6db6fd1]577 if (addr == aligned) {
578 /* Exact block start including alignment. */
579 split_mark(cur, real_size);
[a35b458]580
[207533f]581 next_fit = cur;
[0b37882]582 return addr;
[6db6fd1]583 } else {
[d851f597]584 /* Block start has to be aligned */
[6db6fd1]585 size_t excess = (size_t) (aligned - addr);
[a35b458]586
[6db6fd1]587 if (cur->size >= real_size + excess) {
[0b37882]588 /*
589 * The current block is large enough to fit
590 * data in (including alignment).
591 */
[013a5d7]592 if ((void *) cur > (void *) AREA_FIRST_BLOCK_HEAD(area)) {
[0b37882]593 /*
594 * There is a block before the current block.
595 * This previous block can be enlarged to
596 * compensate for the alignment excess.
597 */
598 heap_block_foot_t *prev_foot = (heap_block_foot_t *)
599 ((void *) cur - sizeof(heap_block_foot_t));
[a35b458]600
[0b37882]601 heap_block_head_t *prev_head = (heap_block_head_t *)
602 ((void *) cur - prev_foot->size);
[a35b458]603
[6db6fd1]604 block_check(prev_head);
[a35b458]605
[6db6fd1]606 size_t reduced_size = cur->size - excess;
[d851f597]607 heap_block_head_t *next_head = ((void *) cur) + excess;
[a35b458]608
[0b37882]609 if ((!prev_head->free) &&
610 (excess >= STRUCT_OVERHEAD)) {
611 /*
612 * The previous block is not free and there
613 * is enough free space left to fill in
614 * a new free block between the previous
615 * and current block.
616 */
617 block_init(cur, excess, true, area);
[d851f597]618 } else {
[0b37882]619 /*
620 * The previous block is free (thus there
621 * is no need to induce additional
622 * fragmentation to the heap) or the
623 * excess is small. Therefore just enlarge
624 * the previous block.
625 */
626 block_init(prev_head, prev_head->size + excess,
627 prev_head->free, area);
[d851f597]628 }
[a35b458]629
[0b37882]630 block_init(next_head, reduced_size, true, area);
[d851f597]631 split_mark(next_head, real_size);
[a35b458]632
[207533f]633 next_fit = next_head;
[0b37882]634 return aligned;
[6db6fd1]635 } else {
[0b37882]636 /*
637 * The current block is the first block
638 * in the heap area. We have to make sure
639 * that the alignment excess is large enough
640 * to fit a new free block just before the
641 * current block.
642 */
[6db6fd1]643 while (excess < STRUCT_OVERHEAD) {
644 aligned += falign;
645 excess += falign;
646 }
[a35b458]647
[d851f597]648 /* Check for current block size again */
[6db6fd1]649 if (cur->size >= real_size + excess) {
650 size_t reduced_size = cur->size - excess;
[0b37882]651 cur = (heap_block_head_t *)
[013a5d7]652 (AREA_FIRST_BLOCK_HEAD(area) + excess);
[a35b458]653
[013a5d7]654 block_init((void *) AREA_FIRST_BLOCK_HEAD(area),
655 excess, true, area);
[0b37882]656 block_init(cur, reduced_size, true, area);
[6db6fd1]657 split_mark(cur, real_size);
[a35b458]658
[207533f]659 next_fit = cur;
[0b37882]660 return aligned;
[6db6fd1]661 }
662 }
663 }
664 }
665 }
[0b37882]666 }
[a35b458]667
[0b37882]668 return NULL;
669}
670
[7aafdb86]671/** Try to enlarge any of the heap areas.
672 *
[da287d1]673 * If successful, allocate block of the given size in the area.
[7aafdb86]674 * Should be called only inside the critical section.
675 *
[da287d1]676 * @param size Gross size of item to allocate (bytes).
[7aafdb86]677 * @param align Memory address alignment.
678 *
[da287d1]679 * @return Allocated block.
680 * @return NULL on failure.
681 *
[7aafdb86]682 */
683static void *heap_grow_and_alloc(size_t size, size_t align)
684{
685 if (size == 0)
686 return NULL;
[a35b458]687
[7aafdb86]688 /* First try to enlarge some existing area */
689 for (heap_area_t *area = first_heap_area; area != NULL;
690 area = area->next) {
[a35b458]691
[7aafdb86]692 if (area_grow(area, size + align)) {
[da287d1]693 heap_block_head_t *first =
694 (heap_block_head_t *) AREA_LAST_BLOCK_HEAD(area);
[a35b458]695
[da287d1]696 void *addr =
697 malloc_area(area, first, NULL, size, align);
[7aafdb86]698 malloc_assert(addr != NULL);
699 return addr;
700 }
701 }
[a35b458]702
[7aafdb86]703 /* Eventually try to create a new area */
704 if (area_create(AREA_OVERHEAD(size + align))) {
[da287d1]705 heap_block_head_t *first =
706 (heap_block_head_t *) AREA_FIRST_BLOCK_HEAD(last_heap_area);
[a35b458]707
[da287d1]708 void *addr =
709 malloc_area(last_heap_area, first, NULL, size, align);
[7aafdb86]710 malloc_assert(addr != NULL);
711 return addr;
712 }
[a35b458]713
[7aafdb86]714 return NULL;
715}
716
[0b37882]717/** Allocate a memory block
718 *
719 * Should be called only inside the critical section.
720 *
721 * @param size The size of the block to allocate.
722 * @param align Memory address alignment.
723 *
724 * @return Address of the allocated block or NULL on not enough memory.
725 *
726 */
727static void *malloc_internal(const size_t size, const size_t align)
728{
[13f2461]729 malloc_assert(first_heap_area != NULL);
[a35b458]730
[0b37882]731 if (align == 0)
732 return NULL;
[a35b458]733
[0b37882]734 size_t falign = lcm(align, BASE_ALIGN);
[a35b458]735
[e7c3fa0]736 /* Check for integer overflow. */
737 if (falign < align)
738 return NULL;
[a35b458]739
[c828803]740 /*
741 * The size of the allocated block needs to be naturally
742 * aligned, because the footer structure also needs to reside
743 * on a naturally aligned address in order to avoid unaligned
744 * memory accesses.
745 */
[da287d1]746 size_t gross_size = GROSS_SIZE(ALIGN_UP(size, BASE_ALIGN));
[a35b458]747
[0b37882]748 /* Try the next fit approach */
[da287d1]749 heap_block_head_t *split = next_fit;
[a35b458]750
[0b37882]751 if (split != NULL) {
[7aafdb86]752 void *addr = malloc_area(split->area, split, NULL, gross_size,
[0b37882]753 falign);
[a35b458]754
[0b37882]755 if (addr != NULL)
756 return addr;
[6db6fd1]757 }
[a35b458]758
[0b37882]759 /* Search the entire heap */
[013a5d7]760 for (heap_area_t *area = first_heap_area; area != NULL;
761 area = area->next) {
[0b37882]762 heap_block_head_t *first = (heap_block_head_t *)
[013a5d7]763 AREA_FIRST_BLOCK_HEAD(area);
[a35b458]764
[7aafdb86]765 void *addr = malloc_area(area, first, split, gross_size,
[0b37882]766 falign);
[a35b458]767
[0b37882]768 if (addr != NULL)
769 return addr;
770 }
[a35b458]771
[7aafdb86]772 /* Finally, try to grow heap space and allocate in the new area. */
773 return heap_grow_and_alloc(gross_size, falign);
[6db6fd1]774}
775
[3292623]776/** Allocate memory
777 *
778 * @param size Number of bytes to allocate.
779 *
780 * @return Allocated memory or NULL.
781 *
782 */
[6db6fd1]783void *malloc(const size_t size)
784{
[af5dfa5b]785 heap_lock();
[3292623]786 void *block = malloc_internal(size, BASE_ALIGN);
[af5dfa5b]787 heap_unlock();
[d54b303]788
[3292623]789 return block;
[6db6fd1]790}
791
[3292623]792/** Allocate memory with specified alignment
793 *
794 * @param align Alignment in byes.
795 * @param size Number of bytes to allocate.
796 *
797 * @return Allocated memory or NULL.
798 *
799 */
[6db6fd1]800void *memalign(const size_t align, const size_t size)
801{
802 if (align == 0)
803 return NULL;
[a35b458]804
[6db6fd1]805 size_t palign =
806 1 << (fnzb(max(sizeof(void *), align) - 1) + 1);
[9a3b469]807
[af5dfa5b]808 heap_lock();
[3292623]809 void *block = malloc_internal(size, palign);
[af5dfa5b]810 heap_unlock();
[9a3b469]811
[3292623]812 return block;
[6db6fd1]813}
814
[3292623]815/** Reallocate memory block
816 *
817 * @param addr Already allocated memory or NULL.
818 * @param size New size of the memory block.
819 *
820 * @return Reallocated memory or NULL.
821 *
822 */
[8ebe212]823void *realloc(void *const addr, const size_t size)
[6db6fd1]824{
[d8023313]825 if (size == 0) {
826 free(addr);
827 return NULL;
828 }
829
[6db6fd1]830 if (addr == NULL)
831 return malloc(size);
[a35b458]832
[af5dfa5b]833 heap_lock();
[a35b458]834
[6db6fd1]835 /* Calculate the position of the header. */
836 heap_block_head_t *head =
837 (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
[a35b458]838
[6db6fd1]839 block_check(head);
[13f2461]840 malloc_assert(!head->free);
[a35b458]841
[0b37882]842 heap_area_t *area = head->area;
[a35b458]843
[0b37882]844 area_check(area);
[13f2461]845 malloc_assert((void *) head >= (void *) AREA_FIRST_BLOCK_HEAD(area));
846 malloc_assert((void *) head < area->end);
[a35b458]847
[6db6fd1]848 void *ptr = NULL;
[3292623]849 bool reloc = false;
[f450280]850 size_t real_size = GROSS_SIZE(ALIGN_UP(size, BASE_ALIGN));
[6db6fd1]851 size_t orig_size = head->size;
[a35b458]852
[6db6fd1]853 if (orig_size > real_size) {
854 /* Shrink */
855 if (orig_size - real_size >= STRUCT_OVERHEAD) {
[0b37882]856 /*
857 * Split the original block to a full block
858 * and a trailing free block.
859 */
860 block_init((void *) head, real_size, false, area);
[6db6fd1]861 block_init((void *) head + real_size,
[0b37882]862 orig_size - real_size, true, area);
[013a5d7]863 heap_shrink(area);
[6db6fd1]864 }
[a35b458]865
[6db6fd1]866 ptr = ((void *) head) + sizeof(heap_block_head_t);
867 } else {
868 heap_block_head_t *next_head =
869 (heap_block_head_t *) (((void *) head) + head->size);
[5b46ec8]870 bool have_next = ((void *) next_head < area->end);
871
872 if (((void *) head) + real_size > area->end) {
873 /*
874 * The current area is too small to hold the resized
875 * block. Make sure there are no used blocks standing
876 * in our way and try to grow the area using real_size
877 * as a safe upper bound.
878 */
879
880 bool have_next_next;
881
882 if (have_next) {
883 have_next_next = (((void *) next_head) +
884 next_head->size < area->end);
885 }
886 if (!have_next || (next_head->free && !have_next_next)) {
887 /*
888 * There is no next block in this area or
889 * it is a free block and there is no used
890 * block following it. There can't be any
891 * free block following it either as
892 * two free blocks would be merged.
893 */
894 (void) area_grow(area, real_size);
895 }
896 }
[a35b458]897
[5b46ec8]898 /*
899 * Look at the next block. If it is free and the size is
900 * sufficient then merge the two. Otherwise just allocate a new
901 * block, copy the original data into it and free the original
902 * block.
903 */
904
905 if (have_next && (head->size + next_head->size >= real_size) &&
906 next_head->free) {
[6db6fd1]907 block_check(next_head);
[5b46ec8]908 block_init(head, head->size + next_head->size, false,
909 area);
[ba0aa6f]910 split_mark(head, real_size);
[a35b458]911
[6db6fd1]912 ptr = ((void *) head) + sizeof(heap_block_head_t);
[207533f]913 next_fit = NULL;
[5b46ec8]914 } else {
[3292623]915 reloc = true;
[5b46ec8]916 }
[3292623]917 }
[a35b458]918
[af5dfa5b]919 heap_unlock();
[a35b458]920
[3292623]921 if (reloc) {
922 ptr = malloc(size);
923 if (ptr != NULL) {
924 memcpy(ptr, addr, NET_SIZE(orig_size));
925 free(addr);
[6db6fd1]926 }
927 }
[a35b458]928
[6db6fd1]929 return ptr;
930}
931
932/** Free a memory block
933 *
934 * @param addr The address of the block.
[3292623]935 *
[6db6fd1]936 */
[8ebe212]937void free(void *const addr)
[6db6fd1]938{
[3019612]939 if (addr == NULL)
940 return;
[a35b458]941
[af5dfa5b]942 heap_lock();
[a35b458]943
[6db6fd1]944 /* Calculate the position of the header. */
[8ebe212]945 heap_block_head_t *head =
946 (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
[a35b458]947
[6db6fd1]948 block_check(head);
[13f2461]949 malloc_assert(!head->free);
[a35b458]950
[0b37882]951 heap_area_t *area = head->area;
[a35b458]952
[0b37882]953 area_check(area);
[13f2461]954 malloc_assert((void *) head >= (void *) AREA_FIRST_BLOCK_HEAD(area));
955 malloc_assert((void *) head < area->end);
[a35b458]956
[6db6fd1]957 /* Mark the block itself as free. */
958 head->free = true;
[a35b458]959
[6db6fd1]960 /* Look at the next block. If it is free, merge the two. */
[8ebe212]961 heap_block_head_t *next_head =
962 (heap_block_head_t *) (((void *) head) + head->size);
[a35b458]963
[0b37882]964 if ((void *) next_head < area->end) {
[6db6fd1]965 block_check(next_head);
966 if (next_head->free)
[0b37882]967 block_init(head, head->size + next_head->size, true, area);
[6db6fd1]968 }
[a35b458]969
[6db6fd1]970 /* Look at the previous block. If it is free, merge the two. */
[013a5d7]971 if ((void *) head > (void *) AREA_FIRST_BLOCK_HEAD(area)) {
[6db6fd1]972 heap_block_foot_t *prev_foot =
973 (heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t));
[a35b458]974
[6db6fd1]975 heap_block_head_t *prev_head =
976 (heap_block_head_t *) (((void *) head) - prev_foot->size);
[a35b458]977
[6db6fd1]978 block_check(prev_head);
[a35b458]979
[6db6fd1]980 if (prev_head->free)
[0b37882]981 block_init(prev_head, prev_head->size + head->size, true,
982 area);
[6db6fd1]983 }
[a35b458]984
[013a5d7]985 heap_shrink(area);
[a35b458]986
[af5dfa5b]987 heap_unlock();
[6db6fd1]988}
989
[1b3e854]990void *heap_check(void)
991{
[af5dfa5b]992 heap_lock();
[a35b458]993
[1b3e854]994 if (first_heap_area == NULL) {
[af5dfa5b]995 heap_unlock();
[1b3e854]996 return (void *) -1;
997 }
[a35b458]998
[1b3e854]999 /* Walk all heap areas */
1000 for (heap_area_t *area = first_heap_area; area != NULL;
1001 area = area->next) {
[a35b458]1002
[1b3e854]1003 /* Check heap area consistency */
1004 if ((area->magic != HEAP_AREA_MAGIC) ||
1005 ((void *) area != area->start) ||
1006 (area->start >= area->end) ||
1007 (((uintptr_t) area->start % PAGE_SIZE) != 0) ||
1008 (((uintptr_t) area->end % PAGE_SIZE) != 0)) {
[af5dfa5b]1009 heap_unlock();
[1b3e854]1010 return (void *) area;
1011 }
[a35b458]1012
[1b3e854]1013 /* Walk all heap blocks */
1014 for (heap_block_head_t *head = (heap_block_head_t *)
1015 AREA_FIRST_BLOCK_HEAD(area); (void *) head < area->end;
1016 head = (heap_block_head_t *) (((void *) head) + head->size)) {
[a35b458]1017
[1b3e854]1018 /* Check heap block consistency */
1019 if (head->magic != HEAP_BLOCK_HEAD_MAGIC) {
[af5dfa5b]1020 heap_unlock();
[1b3e854]1021 return (void *) head;
1022 }
[a35b458]1023
[1b3e854]1024 heap_block_foot_t *foot = BLOCK_FOOT(head);
[a35b458]1025
[1b3e854]1026 if ((foot->magic != HEAP_BLOCK_FOOT_MAGIC) ||
1027 (head->size != foot->size)) {
[af5dfa5b]1028 heap_unlock();
[1b3e854]1029 return (void *) foot;
1030 }
1031 }
1032 }
[a35b458]1033
[af5dfa5b]1034 heap_unlock();
[a35b458]1035
[1b3e854]1036 return NULL;
1037}
1038
[6db6fd1]1039/** @}
1040 */
Note: See TracBrowser for help on using the repository browser.