source: mainline/uspace/lib/c/generic/malloc.c@ 63f8966

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 63f8966 was 63f8966, checked in by Martin Decky <martin@…>, 15 years ago

rename library directories (the common "lib" prefix is already in the upper directory)

  • Property mode set to 100644
File size: 12.7 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 <bool.h>
38#include <as.h>
39#include <align.h>
40#include <macros.h>
41#include <assert.h>
42#include <errno.h>
43#include <bitops.h>
44#include <mem.h>
45#include <adt/gcdlcm.h>
46
47/* Magic used in heap headers. */
48#define HEAP_BLOCK_HEAD_MAGIC 0xBEEF0101
49
50/* Magic used in heap footers. */
51#define HEAP_BLOCK_FOOT_MAGIC 0xBEEF0202
52
53/** Allocation alignment (this also covers the alignment of fields
54 in the heap header and footer) */
55#define BASE_ALIGN 16
56
57/**
58 * Either 4 * 256M on 32-bit architecures or 16 * 256M on 64-bit architectures
59 */
60#define MAX_HEAP_SIZE (sizeof(uintptr_t) << 28)
61
62/**
63 *
64 */
65#define STRUCT_OVERHEAD (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
66
67/**
68 * Calculate real size of a heap block (with header and footer)
69 */
70#define GROSS_SIZE(size) ((size) + STRUCT_OVERHEAD)
71
72/**
73 * Calculate net size of a heap block (without header and footer)
74 */
75#define NET_SIZE(size) ((size) - STRUCT_OVERHEAD)
76
77
78/** Header of a heap block
79 *
80 */
81typedef struct {
82 /* Size of the block (including header and footer) */
83 size_t size;
84
85 /* Indication of a free block */
86 bool free;
87
88 /* A magic value to detect overwrite of heap header */
89 uint32_t magic;
90} heap_block_head_t;
91
92/** Footer of a heap block
93 *
94 */
95typedef struct {
96 /* Size of the block (including header and footer) */
97 size_t size;
98
99 /* A magic value to detect overwrite of heap footer */
100 uint32_t magic;
101} heap_block_foot_t;
102
103/** Linker heap symbol */
104extern char _heap;
105
106/** Address of heap start */
107static void *heap_start = 0;
108
109/** Address of heap end */
110static void *heap_end = 0;
111
112/** Maximum heap size */
113static size_t max_heap_size = (size_t) -1;
114
115/** Current number of pages of heap area */
116static size_t heap_pages = 0;
117
118/** Initialize a heap block
119 *
120 * Fills in the structures related to a heap block.
121 *
122 * @param addr Address of the block.
123 * @param size Size of the block including the header and the footer.
124 * @param free Indication of a free block.
125 *
126 */
127static void block_init(void *addr, size_t size, bool free)
128{
129 /* Calculate the position of the header and the footer */
130 heap_block_head_t *head = (heap_block_head_t *) addr;
131 heap_block_foot_t *foot =
132 (heap_block_foot_t *) (addr + size - sizeof(heap_block_foot_t));
133
134 head->size = size;
135 head->free = free;
136 head->magic = HEAP_BLOCK_HEAD_MAGIC;
137
138 foot->size = size;
139 foot->magic = HEAP_BLOCK_FOOT_MAGIC;
140}
141
142/** Check a heap block
143 *
144 * Verifies that the structures related to a heap block still contain
145 * the magic constants. This helps detect heap corruption early on.
146 *
147 * @param addr Address of the block.
148 *
149 */
150static void block_check(void *addr)
151{
152 heap_block_head_t *head = (heap_block_head_t *) addr;
153
154 assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
155
156 heap_block_foot_t *foot =
157 (heap_block_foot_t *) (addr + head->size - sizeof(heap_block_foot_t));
158
159 assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
160 assert(head->size == foot->size);
161}
162
163static bool grow_heap(size_t size)
164{
165 if (size == 0)
166 return false;
167
168 if ((heap_start + size < heap_start) || (heap_end + size < heap_end))
169 return false;
170
171 size_t heap_size = (size_t) (heap_end - heap_start);
172
173 if ((max_heap_size != (size_t) -1) && (heap_size + size > max_heap_size))
174 return false;
175
176 size_t pages = (size - 1) / PAGE_SIZE + 1;
177
178 if (as_area_resize((void *) &_heap, (heap_pages + pages) * PAGE_SIZE, 0)
179 == EOK) {
180 void *end = (void *) ALIGN_DOWN(((uintptr_t) &_heap) +
181 (heap_pages + pages) * PAGE_SIZE, BASE_ALIGN);
182 block_init(heap_end, end - heap_end, true);
183 heap_pages += pages;
184 heap_end = end;
185 return true;
186 }
187
188 return false;
189}
190
191static void shrink_heap(void)
192{
193 // TODO
194}
195
196/** Initialize the heap allocator
197 *
198 * Finds how much physical memory we have and creates
199 * the heap management structures that mark the whole
200 * physical memory as a single free block.
201 *
202 */
203void __heap_init(void)
204{
205 if (as_area_create((void *) &_heap, PAGE_SIZE,
206 AS_AREA_WRITE | AS_AREA_READ)) {
207 heap_pages = 1;
208 heap_start = (void *) ALIGN_UP((uintptr_t) &_heap, BASE_ALIGN);
209 heap_end =
210 (void *) ALIGN_DOWN(((uintptr_t) &_heap) + PAGE_SIZE, BASE_ALIGN);
211
212 /* Make the entire area one large block. */
213 block_init(heap_start, heap_end - heap_start, true);
214 }
215}
216
217uintptr_t get_max_heap_addr(void)
218{
219 if (max_heap_size == (size_t) -1)
220 max_heap_size =
221 max((size_t) (heap_end - heap_start), MAX_HEAP_SIZE);
222
223 return ((uintptr_t) heap_start + max_heap_size);
224}
225
226static void split_mark(heap_block_head_t *cur, const size_t size)
227{
228 assert(cur->size >= size);
229
230 /* See if we should split the block. */
231 size_t split_limit = GROSS_SIZE(size);
232
233 if (cur->size > split_limit) {
234 /* Block big enough -> split. */
235 void *next = ((void *) cur) + size;
236 block_init(next, cur->size - size, true);
237 block_init(cur, size, false);
238 } else {
239 /* Block too small -> use as is. */
240 cur->free = false;
241 }
242}
243
244/** Allocate a memory block
245 *
246 * @param size The size of the block to allocate.
247 * @param align Memory address alignment.
248 *
249 * @return the address of the block or NULL when not enough memory.
250 *
251 */
252static void *malloc_internal(const size_t size, const size_t align)
253{
254 if (align == 0)
255 return NULL;
256
257 size_t falign = lcm(align, BASE_ALIGN);
258 size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign));
259
260 bool grown = false;
261 void *result;
262
263loop:
264 result = NULL;
265 heap_block_head_t *cur = (heap_block_head_t *) heap_start;
266
267 while ((result == NULL) && ((void *) cur < heap_end)) {
268 block_check(cur);
269
270 /* Try to find a block that is free and large enough. */
271 if ((cur->free) && (cur->size >= real_size)) {
272 /* We have found a suitable block.
273 Check for alignment properties. */
274 void *addr = ((void *) cur) + sizeof(heap_block_head_t);
275 void *aligned = (void *) ALIGN_UP(addr, falign);
276
277 if (addr == aligned) {
278 /* Exact block start including alignment. */
279 split_mark(cur, real_size);
280 result = addr;
281 } else {
282 /* Block start has to be aligned */
283 size_t excess = (size_t) (aligned - addr);
284
285 if (cur->size >= real_size + excess) {
286 /* The current block is large enough to fit
287 data in including alignment */
288 if ((void *) cur > heap_start) {
289 /* There is a block before the current block.
290 This previous block can be enlarged to compensate
291 for the alignment excess */
292 heap_block_foot_t *prev_foot =
293 ((void *) cur) - sizeof(heap_block_foot_t);
294
295 heap_block_head_t *prev_head =
296 (heap_block_head_t *) (((void *) cur) - prev_foot->size);
297
298 block_check(prev_head);
299
300 size_t reduced_size = cur->size - excess;
301 heap_block_head_t *next_head = ((void *) cur) + excess;
302
303 if ((!prev_head->free) && (excess >= STRUCT_OVERHEAD)) {
304 /* The previous block is not free and there is enough
305 space to fill in a new free block between the previous
306 and current block */
307 block_init(cur, excess, true);
308 } else {
309 /* The previous block is free (thus there is no need to
310 induce additional fragmentation to the heap) or the
311 excess is small, thus just enlarge the previous block */
312 block_init(prev_head, prev_head->size + excess, prev_head->free);
313 }
314
315 block_init(next_head, reduced_size, true);
316 split_mark(next_head, real_size);
317 result = aligned;
318 cur = next_head;
319 } else {
320 /* The current block is the first block on the heap.
321 We have to make sure that the alignment excess
322 is large enough to fit a new free block just
323 before the current block */
324 while (excess < STRUCT_OVERHEAD) {
325 aligned += falign;
326 excess += falign;
327 }
328
329 /* Check for current block size again */
330 if (cur->size >= real_size + excess) {
331 size_t reduced_size = cur->size - excess;
332 cur = (heap_block_head_t *) (heap_start + excess);
333
334 block_init(heap_start, excess, true);
335 block_init(cur, reduced_size, true);
336 split_mark(cur, real_size);
337 result = aligned;
338 }
339 }
340 }
341 }
342 }
343
344 /* Advance to the next block. */
345 cur = (heap_block_head_t *) (((void *) cur) + cur->size);
346 }
347
348 if ((result == NULL) && (!grown)) {
349 if (grow_heap(real_size)) {
350 grown = true;
351 goto loop;
352 }
353 }
354
355 return result;
356}
357
358void *calloc(const size_t nmemb, const size_t size)
359{
360 void *block = malloc(nmemb * size);
361 if (block == NULL)
362 return NULL;
363
364 memset(block, 0, nmemb * size);
365 return block;
366}
367
368void *malloc(const size_t size)
369{
370 return malloc_internal(size, BASE_ALIGN);
371}
372
373void *memalign(const size_t align, const size_t size)
374{
375 if (align == 0)
376 return NULL;
377
378 size_t palign =
379 1 << (fnzb(max(sizeof(void *), align) - 1) + 1);
380
381 return malloc_internal(size, palign);
382}
383
384void *realloc(const void *addr, const size_t size)
385{
386 if (addr == NULL)
387 return malloc(size);
388
389 /* Calculate the position of the header. */
390 heap_block_head_t *head =
391 (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
392
393 assert((void *) head >= heap_start);
394 assert((void *) head < heap_end);
395
396 block_check(head);
397 assert(!head->free);
398
399 void *ptr = NULL;
400 size_t real_size = GROSS_SIZE(ALIGN_UP(size, BASE_ALIGN));
401 size_t orig_size = head->size;
402
403 if (orig_size > real_size) {
404 /* Shrink */
405 if (orig_size - real_size >= STRUCT_OVERHEAD) {
406 /* Split the original block to a full block
407 and a trailing free block */
408 block_init((void *) head, real_size, false);
409 block_init((void *) head + real_size,
410 orig_size - real_size, true);
411 shrink_heap();
412 }
413
414 ptr = ((void *) head) + sizeof(heap_block_head_t);
415 } else {
416 /* Look at the next block. If it is free and the size is
417 sufficient then merge the two. */
418 heap_block_head_t *next_head =
419 (heap_block_head_t *) (((void *) head) + head->size);
420
421 if (((void *) next_head < heap_end) &&
422 (head->size + next_head->size >= real_size) &&
423 (next_head->free)) {
424 block_check(next_head);
425 block_init(head, head->size + next_head->size, false);
426 split_mark(head, real_size);
427
428 ptr = ((void *) head) + sizeof(heap_block_head_t);
429 } else {
430 ptr = malloc(size);
431 if (ptr != NULL) {
432 memcpy(ptr, addr, NET_SIZE(orig_size));
433 free(addr);
434 }
435 }
436 }
437
438 return ptr;
439}
440
441/** Free a memory block
442 *
443 * @param addr The address of the block.
444 */
445void free(const void *addr)
446{
447 /* Calculate the position of the header. */
448 heap_block_head_t *head
449 = (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
450
451 assert((void *) head >= heap_start);
452 assert((void *) head < heap_end);
453
454 block_check(head);
455 assert(!head->free);
456
457 /* Mark the block itself as free. */
458 head->free = true;
459
460 /* Look at the next block. If it is free, merge the two. */
461 heap_block_head_t *next_head
462 = (heap_block_head_t *) (((void *) head) + head->size);
463
464 if ((void *) next_head < heap_end) {
465 block_check(next_head);
466 if (next_head->free)
467 block_init(head, head->size + next_head->size, true);
468 }
469
470 /* Look at the previous block. If it is free, merge the two. */
471 if ((void *) head > heap_start) {
472 heap_block_foot_t *prev_foot =
473 (heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t));
474
475 heap_block_head_t *prev_head =
476 (heap_block_head_t *) (((void *) head) - prev_foot->size);
477
478 block_check(prev_head);
479
480 if (prev_head->free)
481 block_init(prev_head, prev_head->size + head->size, true);
482 }
483
484 shrink_heap();
485}
486
487/** @}
488 */
Note: See TracBrowser for help on using the repository browser.