blob: c67bc2554e1a1a7ada476057916f984624ac6d9c [file] [log] [blame]
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07001/*
2 * Copyright (c) 2008 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
24#include <err.h>
25#include <list.h>
26#include <rand.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -070027#include <string.h>
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070028#include <kernel/thread.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -070029#include <lib/heap.h>
30
31#define LOCAL_TRACE 0
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070032
33#define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
34
35#define HEAP_MAGIC 'HEAP'
36
Travis Geiselbrechteb946052008-09-07 22:32:49 -070037#if WITH_STATIC_HEAP
38
39#if !defined(HEAP_START) || !defined(HEAP_LEN)
40#error WITH_STATIC_HEAP set but no HEAP_START or HEAP_LEN defined
41#endif
42
43#else
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070044// end of the binary
45extern int _end;
46
47// end of memory
48extern int _end_of_ram;
49
Travis Geiselbrechteb946052008-09-07 22:32:49 -070050#define HEAP_START ((unsigned long)&_end)
51#define HEAP_LEN ((size_t)&_end_of_ram - (size_t)&_end)
52#endif
53
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070054struct free_heap_chunk {
55 struct list_node node;
56 size_t len;
57};
58
59struct heap {
60 void *base;
61 size_t len;
62 struct list_node free_list;
63};
64
65// heap static vars
66static struct heap theheap;
67
68// structure placed at the beginning every allocation
69struct alloc_struct_begin {
70 unsigned int magic;
71 void *ptr;
72 size_t size;
73};
74
75static void dump_free_chunk(struct free_heap_chunk *chunk)
76{
Travis Geiselbrechteb946052008-09-07 22:32:49 -070077 dprintf(INFO, "\t\tbase %p, end 0x%lx, len 0x%zx\n", chunk, (vaddr_t)chunk + chunk->len, chunk->len);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070078}
79
80static void heap_dump(void)
81{
Travis Geiselbrechteb946052008-09-07 22:32:49 -070082 dprintf(INFO, "Heap dump:\n");
83 dprintf(INFO, "\tbase %p, len 0x%zx\n", theheap.base, theheap.len);
84 dprintf(INFO, "\tfree list:\n");
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070085
86 struct free_heap_chunk *chunk;
87 list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
88 dump_free_chunk(chunk);
89 }
90}
91
92static void heap_test(void)
93{
94 void *ptr[16];
95
96 ptr[0] = heap_alloc(8, 0);
97 ptr[1] = heap_alloc(32, 0);
98 ptr[2] = heap_alloc(7, 0);
99 ptr[3] = heap_alloc(0, 0);
100 ptr[4] = heap_alloc(98713, 0);
101 ptr[5] = heap_alloc(16, 0);
102
103 heap_free(ptr[5]);
104 heap_free(ptr[1]);
105 heap_free(ptr[3]);
106 heap_free(ptr[0]);
107 heap_free(ptr[4]);
108 heap_free(ptr[2]);
109
110 heap_dump();
111
112 int i;
113 for (i=0; i < 16; i++)
114 ptr[i] = 0;
115
116 for (i=0; i < 32768; i++) {
117 unsigned int index = (unsigned int)rand() % 16;
118
119 if ((i % (16*1024)) == 0)
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700120 printf("pass %d\n", i);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700121
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700122// printf("index 0x%x\n", index);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700123 if (ptr[index]) {
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700124// printf("freeing ptr[0x%x] = %p\n", index, ptr[index]);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700125 heap_free(ptr[index]);
126 ptr[index] = 0;
127 }
128 unsigned int align = 1 << ((unsigned int)rand() % 8);
129 ptr[index] = heap_alloc((unsigned int)rand() % 32768, align);
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700130// printf("ptr[0x%x] = %p, align 0x%x\n", index, ptr[index], align);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700131
132 DEBUG_ASSERT(((addr_t)ptr[index] % align) == 0);
133// heap_dump();
134 }
135
136 for (i=0; i < 16; i++) {
137 if (ptr[i])
138 heap_free(ptr[i]);
139 }
140
141 heap_dump();
142}
143
144// try to insert this free chunk into the free list, consuming the chunk by merging it with
145// nearby ones if possible. Returns base of whatever chunk it became in the list.
146static struct free_heap_chunk *heap_insert_free_chunk(struct free_heap_chunk *chunk)
147{
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700148#if DEBUGLEVEL > INFO
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700149 vaddr_t chunk_end = (vaddr_t)chunk + chunk->len;
150#endif
151
152// dprintf("%s: chunk ptr %p, size 0x%lx, chunk_end 0x%x\n", __FUNCTION__, chunk, chunk->len, chunk_end);
153
154 struct free_heap_chunk *next_chunk;
155 struct free_heap_chunk *last_chunk;
156
157 // walk through the list, finding the node to insert before
158 list_for_every_entry(&theheap.free_list, next_chunk, struct free_heap_chunk, node) {
159 if (chunk < next_chunk) {
160 DEBUG_ASSERT(chunk_end <= (vaddr_t)next_chunk);
161
162 list_add_before(&next_chunk->node, &chunk->node);
163
164 goto try_merge;
165 }
166 }
167
168 // walked off the end of the list, add it at the tail
169 list_add_tail(&theheap.free_list, &chunk->node);
170
171 // try to merge with the previous chunk
172try_merge:
173 last_chunk = list_prev_type(&theheap.free_list, &chunk->node, struct free_heap_chunk, node);
174 if (last_chunk) {
175 if ((vaddr_t)last_chunk + last_chunk->len == (vaddr_t)chunk) {
176 // easy, just extend the previous chunk
177 last_chunk->len += chunk->len;
178
179 // remove ourself from the list
180 list_delete(&chunk->node);
181
182 // set the chunk pointer to the newly extended chunk, in case
183 // it needs to merge with the next chunk below
184 chunk = last_chunk;
185 }
186 }
187
188 // try to merge with the next chunk
189 if (next_chunk) {
190 if ((vaddr_t)chunk + chunk->len == (vaddr_t)next_chunk) {
191 // extend our chunk
192 chunk->len += next_chunk->len;
193
194 // remove them from the list
195 list_delete(&next_chunk->node);
196 }
197 }
198
199 return chunk;
200}
201
202struct free_heap_chunk *heap_create_free_chunk(void *ptr, size_t len)
203{
204 DEBUG_ASSERT((len % sizeof(void *)) == 0); // size must be aligned on pointer boundary
205
206 struct free_heap_chunk *chunk = (struct free_heap_chunk *)ptr;
207 chunk->len = len;
208
209 return chunk;
210}
211
212void *heap_alloc(size_t size, unsigned int alignment)
213{
214 void *ptr;
215
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700216 LTRACEF("size %zd, align %d\n", size, alignment);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700217
218 // alignment must be power of 2
219 if (alignment & (alignment - 1))
220 return NULL;
221
222 // we always put a size field + base pointer + magic in front of the allocation
223 size += sizeof(struct alloc_struct_begin);
224
225 // make sure we allocate at least the size of a struct free_heap_chunk so that
226 // when we free it, we can create a struct free_heap_chunk struct and stick it
227 // in the spot
228 if (size < sizeof(struct free_heap_chunk))
229 size = sizeof(struct free_heap_chunk);
230
231 // round up size to a multiple of native pointer size
232 size = ROUNDUP(size, sizeof(void *));
233
234 // deal with nonzero alignments
235 if (alignment > 0) {
236 if (alignment < 16)
237 alignment = 16;
238
239 // add alignment for worst case fit
240 size += alignment;
241 }
242
243 // critical section
244 enter_critical_section();
245
246 // walk through the list
247 ptr = NULL;
248 struct free_heap_chunk *chunk;
249 list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
250 DEBUG_ASSERT((chunk->len % sizeof(void *)) == 0); // len should always be a multiple of pointer size
251
252 // is it big enough to service our allocation?
253 if (chunk->len >= size) {
254 ptr = chunk;
255
256 // remove it from the list
257 struct list_node *next_node = list_next(&theheap.free_list, &chunk->node);
258 list_delete(&chunk->node);
259
260 if (chunk->len > size + sizeof(struct free_heap_chunk)) {
261 // there's enough space in this chunk to create a new one after the allocation
262 struct free_heap_chunk *newchunk = heap_create_free_chunk((uint8_t *)ptr + size, chunk->len - size);
263
264 // truncate this chunk
265 chunk->len -= chunk->len - size;
266
267 // add the new one where chunk used to be
268 if (next_node)
269 list_add_before(next_node, &newchunk->node);
270 else
271 list_add_tail(&theheap.free_list, &newchunk->node);
272 }
273
274 // the allocated size is actually the length of this chunk, not the size requested
275 DEBUG_ASSERT(chunk->len >= size);
276 size = chunk->len;
277
278 ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
279
280 // align the output if requested
281 if (alignment > 0) {
282 ptr = (void *)ROUNDUP((addr_t)ptr, alignment);
283 }
284
285 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
286 as--;
287 as->magic = HEAP_MAGIC;
288 as->ptr = (void *)chunk;
289 as->size = size;
290
291 break;
292 }
293 }
294
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700295 LTRACEF("returning ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700296
297// heap_dump();
298
299 exit_critical_section();
300
301 return ptr;
302}
303
304void heap_free(void *ptr)
305{
306 if (ptr == 0)
307 return;
308
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700309 LTRACEF("ptr %p\n", ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700310
311 // check for the old allocation structure
312 struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
313 as--;
314
315 DEBUG_ASSERT(as->magic == HEAP_MAGIC);
316
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700317 LTRACEF("allocation was %zd bytes long at ptr %p\n", as->size, as->ptr);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700318
319 // looks good, create a free chunk and add it to the pool
320 enter_critical_section();
321 heap_insert_free_chunk(heap_create_free_chunk(as->ptr, as->size));
322 exit_critical_section();
323
324// heap_dump();
325}
326
327void heap_init(void)
328{
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700329 LTRACE_ENTRY;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700330
331 // set the heap range
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700332 theheap.base = (void *)HEAP_START;
333 theheap.len = HEAP_LEN;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700334
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700335 LTRACEF("base %p size %zd bytes\n", theheap.base, theheap.len);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700336
337 // initialize the free list
338 list_initialize(&theheap.free_list);
339
340 // create an initial free chunk
341 heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
342
343 // dump heap info
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700344// heap_dump();
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700345
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700346// dprintf(INFO, "running heap tests\n");
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700347// heap_test();
348}
349
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700350#if DEBUGLEVEL > 1
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800351#if WITH_LIB_CONSOLE
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700352
Travis Geiselbrecht39deded2009-01-24 20:12:27 -0800353#include <lib/console.h>
Travis Geiselbrechteb946052008-09-07 22:32:49 -0700354
355static int cmd_heap(int argc, const cmd_args *argv);
356
357STATIC_COMMAND_START
358 { "heap", "heap debug commands", &cmd_heap },
359STATIC_COMMAND_END(heap);
360
361static int cmd_heap(int argc, const cmd_args *argv)
362{
363 if (argc < 2) {
364 printf("not enough arguments\n");
365 return -1;
366 }
367
368 if (strcmp(argv[1].str, "info") == 0) {
369 heap_dump();
370 } else {
371 printf("unrecognized command\n");
372 return -1;
373 }
374
375 return 0;
376}
377
378#endif
379#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700380