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

topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b169619 was ffccdff0, checked in by Martin Decky <martin@…>, 5 years ago

Unify alignment handling

Use the C11 alignof() operator. Make sure the allocation alignment is
sufficient.

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