heap.c 11 KB

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